list是个双向链表。
底层是带头双向循环链表。
元素访问没有[]了,意味着以后大部分需要使用迭代器进行访问。
先试着写一下代码。
#include
using namespace std;
void test1()
{
list lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
list::iterator it = lt.begin();
while(it != lt.end())
{
cout << *it << endl;
it++;
}
for (auto c : lt)
{
cout << c << endl;
}
}
int main()
{
test1();
return 0;
}
到着遍历可以用反向迭代器。
const对象调用const迭代器。
没有reserve和resize了,因为链表是按需要申请和释放空间的。
获取头和尾的数据 。
如果pop_back的数量大于结点数量,会报错。
list中没有find()模板,需要调用algorithm库中的find。
#include
#include
#include
using namespace std;
void test2()
{
list lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
auto pos = find(lt.begin(), lt.end(), 3);
if (pos != lt.end())
{
lt.insert(pos, 30);
}
for (auto i : lt)
{
cout << i <<" ";
}
cout << endl;
}
int main()
{
test2();
return 0;
}
不失效。
但erase后,pos会失效,因为此时pos位置结点已经没了。
底层是快速排序。
链表sort底层是归并排序。
为什么链表有自己的sort?
vector和string属于随机。
底层是快速排序。
它俩能够相减,需要迭代器是连续的,而链表迭代器不连续。
链表list要排序只能使用list的sort了。
用库里面sort对vector排序要比list中的sort对链表排序快。
平时头插和头删最好不用vector 。
remove一个对象中没有的数,不会报错。
利用unique进行去重,必须先保证链表数据有序。
合并
但在合并前,需要保证两个列表数据都是有序的,否则会报错。
void test3()
{
list lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_front(5);
lt.push_front(6);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
list lt1(lt);
for (auto e : lt1)
{
cout << e << " ";
}
cout << endl;
lt.clear();
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
}
这个代码,我们需要写个深拷贝构造函数,因为默认拷贝构造函数是个浅拷贝,lt被clear后,地址被释放了一次,lt1出函数时,地址又被释放了一次,它俩是相同的地址,被释放两次,所以会报错。
简单模拟实现下list
#include
#include
#include
#include
#include
namespace bit
{
using namespace std;
template
struct list_node
{
list_node* _next;
list_node* _prev;
T _data;
list_node(const T& x)
:_next(nullptr)
, _prev(nullptr)
, _data(x)
{}
};
template
struct list_iterator
{
typedef list_node node;
typedef list_iterator self;
node* _pnode;
list_iterator(node* p)
:_pnode(p)
{}
Ref operator*()
{
return _pnode->_data;
}
self& operator++()
{
_pnode = _pnode->_next;
return *this;
}
self& operator--()
{
_pnode = _pnode->_prev;
return *this;
}
bool operator!=(const self& e)
{
return _pnode != e._pnode;
}
};
template
class list
{
public:
typedef list_node node;
typedef list_iterator iterator;
typedef list_iterator const_iterator;
list()
{
empty_initialize();
}
const_iterator begin()const
{
return const_iterator(_head->_next);
}
const_iterator end()const
{
return const_iterator(_head);
}
iterator begin()
{
return iterator(_head->_next);
}
iterator end()
{
return iterator(_head);
}
iterator insert(iterator pos, const T& n)
{
node* newnode = new node(n);
node* cur = pos._pnode;
node* prev = cur->_prev;
newnode->_next = cur;
newnode->_prev = prev;
prev->_next = newnode;
cur->_prev = newnode;
return iterator(newnode);
}
iterator erase(iterator pos)
{
assert(pos != end());
node* prev = pos._pnode->_prev;
node* next = pos._pnode->_next;
prev->_next = next;
next->_prev = prev;
delete pos._pnode;
return iterator(next);
}
list& operator=(const list& lt)
{
if (this != <)
{
clear();
for (const auto& e : lt)
{
push_back(e);
}
}
return *this;
}
void push_front(const T& x)
{
insert(begin(), x);
}
void pop_front()
{
erase(begin());
}
void pop_back()
{
erase(--end());
}
void clear()
{
iterator it = begin();
while (it != end())
{
it = erase(it);
}
}
/*list(const list& lt)
{
_head = new node(T());
_head->_next = _head;
_head->_prev = _head;
for (const auto &e : lt)
{
push_back(e);
}
}*/
void empty_initialize()
{
_head = new node(T());
_head->_next = _head;
_head->_prev = _head;
}
~list()
{
clear();
delete _head;
_head = NULL;
}
void push_back(const T& e)
{
/*node* NEW = new node(e);
node* tail = _head->_prev;
tail->_next = NEW;
NEW->_next = _head;
_head->_prev = NEW;
NEW->_prev = tail;*/
insert(end(), e);
}
private:
node* _head;
};
void test1_1(const list& lt1)
{
list::const_iterator it1 = lt1.begin();
cout << "lt1::";
while (it1 != lt1.end())
{
cout << *it1 << " ";
++it1;
}
cout << endl;
}
void test1()
{
list lt1;
lt1.push_back(1);
lt1.push_back(2);
lt1.push_back(3);
lt1.push_back(4);
lt1.push_back(5);
list::iterator it = lt1.begin();
cout << "lt1:";
while (it != lt1.end())
{
cout << (*it)++ << " ";
++it;
}
cout << endl;
cout << "lt1:";
for (auto e : lt1)
{
cout << e;
}
lt1.insert(lt1.end(), 7);
cout << endl;
cout << "lt1:";
for (auto e : lt1)
{
cout << e;
}
cout << endl;
list lt2 = lt1;
lt1.erase(--lt1.end());
lt1.erase(--lt1.end());
cout << "lt1::";
for (auto e : lt1)
{
cout << e;
}
cout << endl;
cout << "lt2::";
for (auto e : lt2)
{
cout << e;
}
cout << endl;
lt2 = lt1;
cout << "lt1::";
for (auto e : lt1)
{
cout << e;
}
cout << endl;
cout << "lt2::";
for (auto e : lt2)
{
cout << e;
}
cout << endl;
test1_1(lt1);
lt1.push_front(8);
cout << "lt1::";
for (auto e : lt1)
{
cout << e;
}
cout << endl;
lt1.pop_front();
cout << "lt1::";
for (auto e : lt1)
{
cout << e;
}
cout << endl;
lt1.pop_back();
cout << "lt1::";
for (auto e : lt1)
{
cout << e;
}
cout << endl;
lt2.clear();
cout << "lt2::";
for (auto e : lt2)
{
cout << e;
}
cout << endl;
}
}
int main()
{
bit::test3();
return 0;
}
构造函数名和类名相同,list是类名,所以不写list
在类模板内部类型等同于类名,但了解就行,我们尽量不用。
c++中的cout起到了针对自定义类型输出的作用。
因为这个,所以需要提供默认构造。
写两个箭头反而是错的。
可以这样写
当我们用const迭代器对数据修改时,发现它可以修改。这就出现了问题。
这是因为我们的->运算符重载返回的是T*,我们需要把返回值变为const T*,这样结构体内的数据就不能修改了。
注意T*const,时是指针指向不能修改,而指针指向的结构体中的数据能被修改。
我们再加个模板参数就能解决。
从上述过程,我们也看出了typedef的优势。
来看下完整代码。
#include
#include
#include
#include
#include
namespace bit
{
using namespace std;
template
struct list_node
{
list_node* _next;
list_node* _prev;
T _data;
list_node(const T& x)
:_next(nullptr)
, _prev(nullptr)
, _data(x)
{}
};
template
struct list_iterator
{
typedef list_node node;
typedef list_iterator self;
node* _pnode;
list_iterator(node* p)
:_pnode(p)
{}
Ref operator*()
{
return _pnode->_data;
}
self& operator++()
{
_pnode = _pnode->_next;
return *this;
}
self& operator++(int)
{
self tmp(*this);
_pnode = _pnode->_next;
return tmp;
}
self& operator--()
{
_pnode = _pnode->_prev;
return *this;
}
Ptr operator->()
{
return &_pnode->_data;
}
bool operator!=(const self& e)
{
return _pnode != e._pnode;
}
};
template
class list
{
public:
typedef list_node node;
typedef list_iterator iterator;
typedef list_iterator const_iterator;
list()
{
empty_initialize();
}
const_iterator begin()const
{
return const_iterator(_head->_next);
}
const_iterator end()const
{
return const_iterator(_head);
}
iterator begin()
{
return iterator(_head->_next);
}
iterator end()
{
return iterator(_head);
}
iterator insert(iterator pos, const T& n)
{
node* newnode = new node(n);
node* cur = pos._pnode;
node* prev = cur->_prev;
newnode->_next = cur;
newnode->_prev = prev;
prev->_next = newnode;
cur->_prev = newnode;
return iterator(newnode);
}
iterator erase(iterator pos)
{
assert(pos != end());
node* prev = pos._pnode->_prev;
node* next = pos._pnode->_next;
prev->_next = next;
next->_prev = prev;
delete pos._pnode;
pos._pnode;
return iterator(next);
}
/*list& operator=(const list& lt)
{
if (this != <)
{
clear();
for (const auto& e : lt)
{
push_back(e);
}
}
return *this;
}*/
void push_front(const T& x)
{
insert(begin(), x);
}
void pop_front()
{
erase(begin());
}
void pop_back()
{
erase(--end());
}
void clear()
{
iterator it = begin();
while (it != end())
{
it = erase(it);
}
}
/*list(const list& lt)
{
_head = new node(T());
_head->_next = _head;
_head->_prev = _head;
for (const auto &e : lt)
{
push_back(e);
}
}*/
template
list(inter begin, inter end)
{
_head = new node(T());
_head->_next = _head;
_head->_prev = _head;
while (begin != end)
{
push_back(*begin);
++begin;
}
}
void swap(list& tmp)
{
std::swap(_head, tmp._head);
}
list(const list& lt)
{
empty_initialize();
list tmp(lt.begin(), lt.end());
swap(tmp);
}
list operator=(list lt)
{
swap(lt);
return *this;
}
void empty_initialize()
{
_head = new node(T());
_head->_next = _head;
_head->_prev = _head;
}
~list()
{
clear();
delete _head;
_head = NULL;
}
void push_back(const T& e)
{
/*node* NEW = new node(e);
node* tail = _head->_prev;
tail->_next = NEW;
NEW->_next = _head;
_head->_prev = NEW;
NEW->_prev = tail;*/
insert(end(), e);
}
private:
node* _head;
};
struct Pos
{
Pos(int row = 0, int col = 0)
:row(row)
, col(col)
{}
int row;
int col;
};
void test1_2(const list& lt1)
{
list::const_iterator it1 = lt1.begin();
cout << "lt1::";
while (it1 != lt1.end())
{
cout << it1->row << ":" << (*it1).col;
cout << endl;
++it1;
}
}
void testpos()
{
list p1;
p1.push_back(Pos(1, 1));
p1.push_back(Pos(2, 2));
p1.push_back(Pos(3, 3));
p1.push_back(Pos(4, 4));
list::iterator it = p1.begin();
while (it != p1.end())
{
//cout << (*it).row << ":" << (*it).col;
cout << it->row << ":" << (*it).col;
cout << endl;
++it;
}
test1_2(p1);
}
}
int main()
{
bit::testpos();
return 0;
}
初始化列表如果不显示初始化, 自定义类型会调用默认构造函数,内置类型需要自己给缺省值,或在初始化列表显示初始化,否则会被初始化为随机值。
对于析构而言,编译器对自定义类型调用析构函数,对于内置类型不处理。编译器也不敢处理。
比如list的头指针。因为list它不仅有头结点。只处理头结点会出错。
迭代器我们没有写析构函数,_pnode也不会自动释放。
另外之前我们自己模拟实现的迭代器的拷贝构造 其实是浅拷贝。
两个容器指向同一个空间。因为_pnode并不释放,所以浅拷贝不会出问题。
不需要显示写析构,就不需要显示写拷贝构造和赋值。