首先,我們需要把鏈表管理起來,也就是把一個個節點管理起來,但是每個節點的信息我們也需要管理,例如節點的前驅指針和后驅指針,以及節點的值,所以我們這里先封裝兩個類來管理節點和鏈表。
namespace Ro
{template<class T>struct list_node{list_node(const T& val = T()):_data(val),prev(nullptr),next(nullptr){}T _data;list_node<T>* prev;list_node<T>* next;};template<class T>class list{typedef list_node<T> Node;public:list(){_head = new Node;_head->prev = _head;_head->next = _head;_size = 0;}private:Node* _head;size_t _size;};
};
先將list的模子給寫好,同時介紹以下幾點:
list_node:
在list中我們需要經常訪問list_node中的成員變量,所以需要將list_node中的成員變量公有,干脆使用結構體struct,因為struct默認公有。
注意:成員變量的類型list_node<T>需要顯示實例化,不可以直接list_node,雖然之前在函數模板章節有講過模板也可以不顯式的寫,讓編譯器自動推導類型,但是那是函數模板可以讓編譯器推導,在這里是類模板,類模板是不能自動推導類型的,所以需要顯式實例化。
另外在list_node中我們不需要顯式定義拷貝構造函數和析構函數,只需要寫構造函數
原因如下:
1.?默認行為已足夠
- 如果節點類只包含簡單的數據成員(如基本數據類型或指針),默認的拷貝構造函數和析構函數已經能夠正確工作。
- 拷貝構造函數:默認拷貝構造函數會逐成員拷貝?
data
、next
?和?prev
?指針。對于指針成員,這只會復制指針的值(即地址),而不會復制指針指向的對象。 - 析構函數:默認析構函數會自動銷毀?
data
(如果?T
?是一個非指針類型且需要銷毀),但不會自動釋放?next
?和?prev
?指針指向的內存(因為它們只是指針,不負責管理內存)。
2.?避免不必要的復雜性
- 顯式定義拷貝構造函數和析構函數可能會引入復雜性,尤其是當節點類包含動態分配的資源時。
- 如果手動定義拷貝構造函數,需要確保深拷貝邏輯正確實現,否則可能導致懸空指針或雙重釋放等問題。
- 如果手動定義析構函數,需要確保釋放所有動態分配的資源,否則可能導致內存泄漏。
- 通過依賴默認的拷貝構造函數和析構函數,可以避免這些潛在問題。
3.?鏈表本身的內存管理
- 在實現一個鏈表時,通常由鏈表類本身負責管理節點的內存分配和釋放,而不是由節點類負責。
- 例如,鏈表的插入和刪除操作會負責創建和銷毀節點,而節點類只需要存儲數據和指針。
- 因此,節點類不需要顯式定義析構函數來釋放內存。
4.?性能考慮
- 顯式定義拷貝構造函數和析構函數可能會引入額外的開銷,尤其是在節點類被頻繁拷貝或銷毀的情況下。
- 依賴默認的拷貝構造函數和析構函數可以避免這種開銷,尤其是在節點類只包含簡單數據成員的情況下。
5.?指針語義的合理性
- 在鏈表節點中,指針成員(如?
next
?和?prev
)通常只是指向其他節點的指針,而不是擁有這些節點的所有權。 - 因此,默認的淺拷貝行為(即復制指針值)是合理的,因為鏈表類本身會管理節點的生命周期。
6.?C++ 的規則五(Rule of Five)
- 根據 C++ 的規則五,如果一個類需要顯式定義拷貝構造函數、拷貝賦值運算符、移動構造函數、移動賦值運算符或析構函數中的任何一個,那么通常需要顯式定義所有這些函數。
- 如果節點類不需要顯式定義這些函數中的任何一個(因為默認行為已經足夠),那么就沒有必要顯式定義它們。
7.?現代 C++ 的智能指針
- 如果需要更復雜的內存管理,可以在鏈表實現中使用智能指針(如?
std::unique_ptr
?或?std::shared_ptr
)來管理節點的生命周期。 - 例如,可以使用?
std::unique_ptr<list_node<T>>
?來管理節點的內存,從而避免手動管理內存的復雜性。
list:
我們這里實現的雙向帶頭循環鏈表,所以我們需要一個指向哨兵位頭節點的指針,方便我們管理鏈表,另外為方便得到list的size(),省去遍歷一遍鏈表的繁瑣,我們定義兩個成員變量_head和_size。同時在構造函數中,我們初始化頭節點,并把list_node<T>typedef一下為Node。
接下來我們正式來模擬實現list
1. push_back()
還是老規矩,我們先來實現尾插,讓list能夠跑起來
void push_back(const T& data)
{Node* newnode = new Node(data);Node* ptail = _head->prev;ptail->next = newnode;newnode->next = _head;newnode->prev = ptail;_head->prev = newnode;_size++;
}
測試一下:
void test_list1()
{list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);
}
2.迭代器實現
在string和vector中,迭代器是原生指針typedef來的,但是在list中這樣是不行的,因為string和vector是連續的空間,++就能得到下一個數據的地址,對迭代器解引用就能得到數據,但是list空間是不連續的,直接++是不行的,直接解引也是不行的,因為此時指針指向的是一個結構體,對結構體解引用得到的是該結構體的左值引用,但是我們想做到解引用直接拿到結構體中存的數據。
那要怎么做呢?在之前我們有提過,迭代器是一個像原生指針一樣的東西,我們要讓迭代器能夠模擬指針一樣的操作,所以這里我們可以將迭代器封裝成一個類,通過operator++,operator*等運算符重載來模擬指針的行為。
template<class T>
struct list_iterator
{typedef list_node<T> Node;typedef list_iterator<T> Self;Node* _node;list_iterator(Node* node):_node(node){ }};
我們需要獲取list_node中的值和前驅指針以及后驅指針,所以我們可以讓指向list_node的指針_node作為成員變量,同時迭代器++的返回值同樣是迭代器,所以我們也對迭代器類型也typedef一下
2.1 operator*()
T& operator*()
{return _node->_data;
}
直接返回節點的數據
2.2 operator++()
operator++分為前置++和后置++,在前面的章節中我們有講過,前置++和后置++,可以通過占位符來區分。
前置++:
Self& operator++()
{_node = _node->next;return *this;
}
后置++:
Self operator++(int)
{Self tmp(*this);//先儲存原來的值_node = _node->next;//再++return tmp;//返回原來的值
}
后置++需要返回++前的指針,所以我們先拷貝構造儲存一下++前的指針,再++,然后返回tmp
2.3 operator--()
有++就有--,和++一樣也分為前置--和后置--
前置--:
//前置--
Self& operator--()
{_node = _node->prev;return *this;
}
后置--:
//后置--
Self operator--(int)
{Self tmp(*this);_node = _node->prev;return tmp;
}
2.4 operator!=()和operator==()
迭代器是有比較的,例如我們在使用迭代器遍歷容器的時候經常會這樣用:it != lt.end()
所以還需要比較運算符
bool operator!=(const Self& s) const
{return _node != s._node;
}bool operator==(const Self& s) const
{return _node == s._node;
}
2.5 operator->()
我們知道對結構體的解引用還有箭頭運算符,如果我們list_node中的數據_data是自定義類型,例如是一個日期類Date的話,雖然我們可以 { (*it)._year,(*it)._month 和 (*it)._day }這樣的操作來訪問,但是對于 .? 這種對結構體成員訪問,使用?-> 訪問結構體成員還是更為普遍的
T* operator->()
{return &_node->_data;
}
直接取結構體的地址返回就行,因為結構體指針能夠使用->運算符訪問結構體成員
2.6 begin()和end()
typedef list_iterator<T> iterator;
iterator begin()
{//return iterator(_head->next);return _head->next;
}iterator end()
{//return iterator(_head);return _head;
}
這里我們可以返回一個iterator的臨時對象,不過這里也可以直接返回_head->next,讓它自己走隱式類型轉換,begin()指向第一個數據,end()指向最后一個數據的下一個位置,最后一個數據的下一個位置就是頭節點
2.7 測試
迭代器的基本功能已經實現的差不多了,接下來我們來測試一下
void test_list1()
{list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);list<int>::iterator it = lt.begin();while (it != lt.end()){cout << *it << ' ';it++;}cout << endl;//范圍forfor (auto e : lt){cout << e << ' ';}cout << endl;
}
再來測試一下重載的箭頭運算符
void test_list2()
{struct AA{int a1 = 1;int a2 = 2;};list<AA> lt;lt.push_back(AA());lt.push_back(AA());lt.push_back(AA());lt.push_back(AA());list<AA>::iterator it = lt.begin();while (it != lt.end()){//特殊處理,本來應該是兩個->,但是為了可讀性省略了一個->cout << it.operator->()->a1 << ':' << it.operator->()->a2 << endl;cout << it->a1 << ':' << it->a2 << endl;it++;}}
需要注意的是這里本來應該是兩個->才合理的,第一個operator->()返回的是AA*的指針,第二個->是訪問結構體AA的成員,但是為了可讀性,省略了一個->
2.8 const迭代器
上面實現的迭代器是普通迭代器可讀可寫,但是還有const迭代器,可讀不可寫。
那const迭代器要怎么實現呢?其實只需要復用一下迭代器的代碼,然后在修改一下細節就行
const迭代器:
template<class T>
struct const_list_iterator
{typedef list_node<T> Node;typedef const_list_iterator<T> Self;Node* _node;const_list_iterator(Node* node):_node(node){ }//前置++Self& operator++(){_node = _node->next;return *this;}//后置++Self operator++(int){Self tmp(*this);//先儲存原來的值_node = _node->next;//再++return tmp;//返回原來的值}//前置--Self& operator--(){_node = _node->prev;return *this;}//后置--Self operator--(int){Self tmp(*this);_node = _node->prev;return tmp;}bool operator!=(const Self& s) const{return _node != s._node;}bool operator==(const Self& s) const{return _node == s._node;}const T* operator->(){return &_node->_data;}const T& operator*(){return _node->_data;}
};
這里我們只需要將返回值修改,返回const迭代器,const T&和const T*。這樣就可以限制寫的功能
在list中,我們同樣typedef一下const迭代器
template<class T>
class list
{typedef list_node<T> Node;
public:typedef list_iterator<T> iterator;typedef const_list_iterator<T> const_iterator;list(){_head = new Node;_head->prev = _head;_head->next = _head;_size = 0;}iterator begin(){//return iterator(_head->next);return _head->next;}iterator end(){//return iterator(_head);return _head;}const_iterator begin() const{return _head->next;}const_iterator end() const{return _head;}void push_back(const T& data){Node* newnode = new Node(data);Node* ptail = _head->prev;ptail->next = newnode;newnode->next = _head;newnode->prev = ptail;_head->prev = newnode;_size++;}
private:Node* _head;size_t _size;
};
注意:const迭代器是const迭代器修飾的對象是const,可讀不可寫,不是迭代器不可寫,同樣const迭代器的begin()和end()需要用const修飾,與const迭代器兼顧,同時防止意外修改容器中的元素
I 測試一下
這里我們發現報錯了,為什么呢?
因為這里我們定義的lt并非const對象,所以 lt.begin() 和 lt.end() 調用的是普通迭代器,但是我們把普通迭代器賦值給const迭代器的對象 it ,造成權限的放大(it的權限放大為普通迭代器),在之前的章節中,我們有講過權限可以縮小,但是不能放大,所以這里會報錯,同時這里 const迭代器對象 it 和普通迭代器 lt.end() 是不能比較的,因為兩個不同類型的對象不能比較,所以這里會報錯沒有匹配的 != 運算符
那怎么解決呢?由于我們目前實現的list只是一些簡單的功能,像拷貝構造等還沒有實現,不然可以拷貝構造一個const對象?
像這樣的話就不會出問題,但是我們還沒實現拷貝構造,那我們干脆實現一個print的函數專門打印,后面測試的時候也可以直接調用
template<class Container>
void print(const Container& con)
{typename Container::const_iterator it = con.begin();while (it != con.end()){//*it += 10;cout << *it << ' ';it++;}cout << endl;//范圍forfor (auto e : con){cout << e << ' ';}cout << endl;
}
在形參處使用const,所以這里con是const對象,我們要使用const迭代器
這里我們自定義了一個Container容器類,這樣我們傳不同的容器都能打印,不止于list
注意這里需要用typename來聲明一下Container::const_iterator是一個類,不然編譯器會分不清這是類還是靜態成員變量
如果我們對const迭代器的對象修改(*it += 10),編譯器會報錯
只讀不寫的話const迭代器就不會報錯
II 巧用模板實現迭代器
雖然我們直接cv一份代碼修改一下細節就能實現const迭代器,但是這樣寫代碼太冗余了,而且普通迭代器和const迭代器只是在返回值類型上不同,其余都一樣,那有沒有什么更好的方法實現呢?我們來看看STL3.0中是怎么做的
我們可以看到在STL3.0中使用了3個類模板參數,這樣就可以讓編譯器給我們實例化出兩個不同的類,這樣寫確實很妙,那我們也這樣模擬豈不美哉
template<class T, class Ref, class Ptr>
struct list_iterator
{typedef list_node<T> Node;typedef list_iterator<T, Ref, Ptr> Self;Node* _node;list_iterator(Node* node):_node(node){ }//前置++Self& operator++(){_node = _node->next;return *this;}//后置++Self operator++(int){Self tmp(*this);//先儲存原來的值_node = _node->next;//再++return tmp;//返回原來的值}//前置--Self& operator--(){_node = _node->prev;return *this;}//后置--Self operator--(int){Self tmp(*this);_node = _node->prev;return tmp;}bool operator!=(const Self& s){return _node != s._node;}bool operator==(const Self& s){return _node == s._node;}Ptr operator->(){return &_node->_data;}Ref operator*(){return _node->_data;}
};template<class T>
class list
{typedef list_node<T> Node;
public:typedef list_iterator<T, T&, T*> iterator;typedef list_iterator<T, const T&, const T*> const_iterator;
}
這樣我們傳什么樣的類模板,就能實例化出不同的迭代器
3.list的增刪查改
3.1 insert()
iterator insert(iterator pos, const T& data)
{Node* newnode = new Node(data);Node* cur = pos._node;//pos位置的節點Node* pre = cur->prev;//在pos前插入,先找到pos前的節點//pre newnode curpre->next = newnode;newnode->next = cur;newnode->prev = pre;cur->prev = newnode;_size++;return newnode;
}
在pos位置前插入,我們要先找到pos位置和pos位置前的節點,然后再將新節點插入其中
測試一下:
void test_list4()
{list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);list<int>::iterator it = lt.begin();int k = 3;while (k--){it++;}lt.insert(it, 20);print(lt);
}
insert實現好了,我們可以把之前的push_back()直接復用insert()的代碼
push_back:
在頭節點前插入
void push_back(const T& data)
{/*Node* newnode = new Node(data);Node* ptail = _head->prev;ptail->next = newnode;newnode->next = _head;newnode->prev = ptail;_head->prev = newnode;_size++;*/insert(end(), data);
}
push_front:
在begin()前插入
void push_front(const T& data)
{insert(begin(), data);
}
3.2 erase()
刪除pos位置的數據
iterator erase(iterator pos)
{assert(pos != end());Node* nxt = pos._node->next;Node* pre = pos._node->prev;pre->next = nxt;nxt->prev = pre;delete pos._node;_size--;return nxt;
}
注意:不能把哨兵位頭節點刪除了,所以我們這里加一個斷言,如果刪除的是頭節點就斷言報錯
刪除pos位置的數據后,pos位置的迭代器就會失效,所以我們這里返回pos位置的下一個位置的迭代器,這里我們返回nxt節點讓它自己走隱式類型轉換
測試一下:
刪除所有偶數
void test_list5()
{list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);list<int>::iterator it = lt.begin();while (it != lt.end()){if (*it % 2 == 0) it = lt.erase(it);else it++;}print(lt);
}
pop_back:
直接復用erase(),刪除頭節點前的數據
void pop_back()
{erase(_head->prev);
}
pop_front:
刪除頭節點后的數據
void pop_front()
{erase(_head->next);
}
3.3 size()和empty()
size_t size() const
{return _size;
}bool empty() const
{return _size == 0;
}
3.4 front() 和 back()
T& front()
{return *begin();
}T& back()
{return *(--end());
}const T& front() const
{return *begin();
}const T& back() const
{return *(--end());
}
比較簡單就不多介紹了
3.5 clear()
void clear()
{auto it = begin();while (it != end()){it = erase(it);}
}
清除所有節點,但不能清除頭節點,所有我們可以直接復用erase
4.list的拷貝構造和賦值重載
4.1 析構函數
~list()
{clear();delete _head;_head = nullptr;
}
這里直接復用clear(),將所有節點清除,最后釋放頭節點。
4.2拷貝構造
和string,vector一樣,list也有涉及深淺拷貝的問題,如果不寫自己的深拷貝的話,走編譯器自己默認的淺拷貝,那么兩個對象指向的就是同一份鏈表,會導致析構兩次。
這里我們可以試一下淺拷貝:
void test_list7()
{list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);list<int> lt2(lt);print(lt);print(lt2);
}
直接運行崩潰了,所以還是要動手完成自己的深拷貝
但是拷貝構造也得要有自己的頭節點,所以我們要先空初始化,創建一個頭節點,那我們干脆直接將構造函數中的初始化頭節點封裝為一個空初始化的函數,在拷貝構造之前先調用空初始化創造自己的頭節點
void empty_init()
{_head = new Node;_head->prev = _head;_head->next = _head;_size = 0;
}
不過我們要將空初始化給私有,因為我們不希望外面可以調用這個接口
//構造函數
list()
{empty_init();
}//拷貝構造
list(const list<T>& l)
{empty_init();for (auto e : l){push_back(e);}
}
空初始化之后,在遍歷鏈表,尾插重新創建一個自己的鏈表。
4.3賦值重載
list<T>& operator=(list<T> tmp)
{swap(_head, tmp._head);return *this;
}
直接使用現代寫法
4.4其余構造函數
可以看到這里還有兩種構造函數,一個是構造n個值為value的鏈表,另一個是迭代器區間構造
我們都來實現一下
list(int n, const T& value = T())
{empty_init();for (int i = 0; i < n; i++) push_back(value);
}template<class InputIterator>
list(InputIterator first, InputIterator last)
{empty_init();while (first != last){push_back(*first);first++;}
}
迭代器區間構造在vector模擬實現時有講過,這里也不多介紹了
代碼如下
list.h:
#pragma once
#include <iostream>
#include <assert.h>using namespace std;namespace Ro
{template<class T>struct list_node{list_node(const T& val = T()):_data(val),prev(nullptr),next(nullptr){}T _data;list_node<T>* prev;list_node<T>* next;};template<class T, class Ref, class Ptr>struct list_iterator{typedef list_node<T> Node;typedef list_iterator<T, Ref, Ptr> Self;Node* _node;list_iterator(Node* node):_node(node){ }//前置++Self& operator++(){_node = _node->next;return *this;}//后置++Self operator++(int){Self tmp(*this);//先儲存原來的值_node = _node->next;//再++return tmp;//返回原來的值}//前置--Self& operator--(){_node = _node->prev;return *this;}//后置--Self operator--(int){Self tmp(*this);_node = _node->prev;return tmp;}bool operator!=(const Self& s){return _node != s._node;}bool operator==(const Self& s){return _node == s._node;}Ptr operator->(){return &_node->_data;}Ref operator*(){return _node->_data;}};//template<class T>//struct const_list_iterator//{// typedef list_node<T> Node;// typedef const_list_iterator<T> Self;// Node* _node;// const_list_iterator(Node* node)// :_node(node)// { }// //前置++// Self& operator++()// {// _node = _node->next;// return *this;// }// //后置++// Self operator++(int)// {// Self tmp(*this);//先儲存原來的值// _node = _node->next;//再++// return tmp;//返回原來的值// }// //前置--// Self& operator--()// {// _node = _node->prev;// return *this;// }// //后置--// Self operator--(int)// {// Self tmp(*this);// _node = _node->prev;// return tmp;// }// bool operator!=(const Self& s) const// {// return _node != s._node;// }// bool operator==(const Self& s) const// {// return _node == s._node;// }// const T* operator->()// {// return &_node->_data;// }// const T& operator*()// {// return _node->_data;// }//};template<class T>class list{typedef list_node<T> Node;void empty_init(){_head = new Node;_head->prev = _head;_head->next = _head;_size = 0;}public:typedef list_iterator<T, T&, T*> iterator;typedef list_iterator<T, const T&, const T*> const_iterator;//構造函數list(){empty_init();}list(int n, const T& value = T()){empty_init();for (int i = 0; i < n; i++) push_back(value);}//迭代器區間構造template<class InputIterator>list(InputIterator first, InputIterator last){empty_init();while (first != last){push_back(*first);first++;}}//拷貝構造list(const list<T>& l){empty_init();for (auto e : l){push_back(e);}}//析構~list(){clear();delete _head;_head = nullptr;}//賦值重載-現代寫法list<T>& operator=(list<T> tmp){swap(_head, tmp._head);return *this;}iterator begin(){//return iterator(_head->next);return _head->next;}iterator end(){//return iterator(_head);return _head;}const_iterator begin() const{return _head->next;}const_iterator end() const{return _head;}void clear(){auto it = begin();while (it != end()){it = erase(it);}}T& front(){return *begin();}T& back(){return *(--end());}const T& front() const{return *begin();}const T& back() const{return *(--end());}size_t size() const{return _size;}bool empty() const{return _size == 0;}void pop_back(){erase(_head->prev);}void pop_front(){erase(_head->next);}iterator erase(iterator pos){assert(pos != end());Node* nxt = pos._node->next;Node* pre = pos._node->prev;pre->next = nxt;nxt->prev = pre;delete pos._node;_size--;return nxt;}iterator insert(iterator pos, const T& data){Node* newnode = new Node(data);Node* cur = pos._node;//pos位置的節點Node* pre = cur->prev;//在pos前插入,先找到pos前的節點//pre newnode curpre->next = newnode;newnode->next = cur;newnode->prev = pre;cur->prev = newnode;_size++;return newnode;}void push_front(const T& data){insert(begin(), data);}void push_back(const T& data){/*Node* newnode = new Node(data);Node* ptail = _head->prev;ptail->next = newnode;newnode->next = _head;newnode->prev = ptail;_head->prev = newnode;_size++;*/insert(end(), data);}private:Node* _head;size_t _size;};template<class Container>void print(const Container& con){typename Container::const_iterator it = con.begin();while (it != con.end()){//*it += 10;cout << *it << ' ';it++;}cout << endl;//范圍forfor (auto e : con){cout << e << ' ';}cout << endl;}void test_list1(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);list<int>::iterator it = lt.begin();while (it != lt.end()){cout << *it << ' ';it++;}cout << endl;//范圍forfor (auto e : lt){cout << e << ' ';}cout << endl;}void test_list2(){struct AA{int a1 = 1;int a2 = 2;};list<AA> lt;lt.push_back(AA());lt.push_back(AA());lt.push_back(AA());lt.push_back(AA());list<AA>::iterator it = lt.begin();while (it != lt.end()){//特殊處理,本來應該是兩個->,但是為了可讀性省略了一個->cout << it.operator->()->a1 << ':' << it.operator->()->a2 << endl;cout << it->a1 << ':' << it->a2 << endl;it++;}}void test_list3(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);print(lt);}void test_list4(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);list<int>::iterator it = lt.begin();int k = 3;while (k--){it++;}lt.insert(it, 20);print(lt);}void test_list5(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);list<int>::iterator it = lt.begin();while (it != lt.end()){if (*it % 2 == 0) it = lt.erase(it);else it++;}print(lt);}void test_list6(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);cout << lt.front() << endl;cout << lt.back() << endl;//print(lt);}void test_list7(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);list<int> lt2(2);//lt2 = lt;print(lt);print(lt2);}
};