使用
翻阅文档即可。
是什么
带头双向循环链表。
实现
1. 类成员设计
template <class T>
struct list_node
{
T _val;
list_node* _next;
list_node* _prev;
};
template <class T>
class list
{
private:
list_node<T> _head;
};
2. 类方法设计
namespace bacon {
template<class T>
struct listNode {
T _val;
listNode *_next;
listNode *_prev;
listNode(const T &val)
: _val(val),
_next(nullptr),
_prev(nullptr) {}
};
template<class T>
class list {
public:
typedef listNode<T> node;
typedef listIterator<T> iterator;
typedef listConstIterator<T> constIterator;
list() { initialize();}
~list() {}
list(const list<T> &other) {}
list &operator=(list<T> &other) {}
public:
iterator begin() {}
iterator end() {}
constIterator begin() const {}
constIterator end() const {}
void pushBack(const T &val) {}
void popBack() {}
void pushFront(const T &val) {}
iterator insert(iterator posIterator, const T &val) {}
iterator erase(iterator posIterator) {}
iterator find(const T &val) {}
bool empty() {}
void swap(list<T> &other) {}
private:
void initialize() {}
private:
listNode<T> *_dummyHead;
};
}
3. 方法核心逻辑
*数据结构初阶已经实现过,这里只是变成类和对象的版本
namespace bacon {
template<class T>
struct listNode {
T _val;
listNode *_next;
listNode *_prev;
listNode(const T &val)
: _val(val),
_next(nullptr),
_prev(nullptr) {}
};
template<class T>
class list {
public:
typedef listNode<T> node;
typedef listIterator<T> iterator;
typedef listConstIterator<T> constIterator;
list() { initialize(); }
~list() {
node *cur = _dummyHead;
while (cur != _dummyHead) {
node *temp = cur;
cur = cur->_next;
delete temp;
}
_dummyHead = nullptr;
}
list(const list<T> &other) {
initialize();
for (const auto &e: other) pushBack(e);
}
list &operator=(list<T> &other) {
swap(other);
return *this;
}
public:
iterator begin() {}
iterator end() {}
constIterator begin() const {}
constIterator end() const {}
void pushBack(const T &val) {
node *newNode = new node(val);
node *tail = _dummyHead->_prev;
newNode->_next = _dummyHead;
_dummyHead->_prev = newNode;
tail->_next = newNode;
newNode->_prev = tail;
}
void popBack() {
assert(!empty());
node *tail = _dummyHead->_prev;
node *tailPrev = tail->_prev;
tailPrev->_next = _dummyHead;
_dummyHead->_prev = tailPrev;
delete tail;
}
void pushFront(const T &val) {
insert(begin(), val);
}
iterator insert(iterator posIterator, const T &val) {}
iterator erase(iterator posIterator) {}
void print() {
node *cur = _dummyHead->_next;
while (cur != _dummyHead) {
cout << cur->_val << " ";
cur = cur->_next;
}
cout << endl;
}
iterator find(const T &val) {
node *cur = _dummyHead->_next;
while (cur != _dummyHead) {
if (cur->_val == val) return iterator(cur);
cur = cur->_next;
}
return end();
}
bool empty() { return _dummyHead->_next == _dummyHead; }
void swap(list<T> &other) {
std::swap(_dummyHead, other._dummyHead);
}
private:
void initialize() {
_dummyHead = new node(0);
_dummyHead->_next = _dummyHead->_prev = _dummyHead;
}
private:
listNode<T> *_dummyHead;
};
}
list的实现,价值最大的不是这里,而是迭代器……
4. 迭代器(重点)
封装,我主沉浮,何不能至?
封装给了我们很灵活的操作空间。为什么这么说?
迭代器是一种类的内嵌类型,具有指针行为,如果用不了原生指针,那我自己封装一个,自己实现它的指针行为。
这种玩法也叫做迭代器模式,是设计模式的一种:封装底层,隐藏底层细节;统一访问方式,降低使用成本。
template<class T>
struct listIterator {
typedef listNode<T> node;
node *_pnode;
listIterator(node *pnode)
: _pnode(pnode) {
}
T &operator*() { return _pnode->_val; }
T *operator->() { return _pnode; }
listIterator operator++() {
_pnode = _pnode->_next;
return *this;
}
listIterator operator--() {
_pnode = _pnode->_prev;
return *this;
}
bool operator!=(const listIterator<T> &other) { return _pnode != other._pnode; }
};
iterator insert(iterator posIterator, const T &val) {
node *posPrev = posIterator._pnode->_prev;
node *pos = posIterator._pnode;
node *newNode = new node(val);
newNode->_next = pos;
pos->_prev = newNode;
posPrev->_next = newNode;
newNode->_prev = posPrev;
return iterator(newNode);
}
iterator erase(iterator posIterator) {
assert(!empty() && posIterator != end());
node *posPrev = posIterator._pnode->_prev;
node *posNext = posIterator._pnode->_next;
posPrev->_next = posNext;
posNext->_prev = posPrev;
delete posIterator._pnode;
return iterator(posNext);
}
const迭代器(重点)
const迭代器的行为和普通迭代器唯一的区别就是,const迭代器不能对对象进行修改,即不能写;但是能读!const迭代器本身是可以被修改的,但是它指向的对象不能通过const迭代器修改。
有的朋友可能认为const迭代器是这样的:
list<int> lt;
const list<int>::iterator = lt.begin();
这种写法,const保护的是iterator本身,并不能保护iterator指向的对象。
放到内置类型看看:
const int *p1; //保护p1指向的空间
int *const p2; //保护p2本身
const迭代器的行为其实是类似p1,保护指向的空间,但自己无需保护。
而const list<int>::iterator = lt.begin();
这样的写法,其实是类似p2,不符合迭代器的行为。
那我们该怎么实现const迭代器?
其实只需要控制operator*()
和operator->()
的返回值T&
和T*
即可。
怎么控制?
可不可以重载operator*()
和operator->()
?
T &operator*() { return _pnode->_val; }
const T &operator*() const { return _pnode->_val; }
T *operator->() { return _pnode; }
const T *operator->() const { return _pnode; }
其实不行的,为什么呢?
如下的代码,要用什么来调用?需要一个这样的对象:const iterator cit;
const T &operator*() const { return _pnode->_val; }
但我们已经讨论过,这样的对象是不符合const迭代器的行为的。所以这种方案舍弃掉了。
那我们可以再给一个类来作为const迭代器:
template<class T>
struct listConstIterator {
typedef listNode<T> node;
const node *_pnode;
listConstIterator(node *pnode)
: _pnode(pnode) {
}
const T &operator*() { return _pnode->_val; }
const T *operator->() { return _pnode; }
listConstIterator operator++() {
_pnode = _pnode->_next;
return *this;
}
listConstIterator operator--() {
_pnode = _pnode->_prev;
return *this;
}
bool operator!=(const listConstIterator<T> &other) { return _pnode != other._pnode; }
};
但我们发现,普通迭代器和const迭代器的区别就那么一点,却多用了一倍的代码来实现,太冗余了。
大佬给出了另一种方案:通过模版参数把operator*()
和operator->()
的返回值泛化,来者是普通对象,返回T&
和T*
;是const对象,返回const T&
和const T*
。
来看看参考代码吧:
template<class T, class Ref, class Ptr>
struct listIterator {
typedef listNode<T> node;
typedef listIterator<T, T &, T *> Self;
node *_pnode;
listIterator(node *pnode)
: _pnode(pnode) {
}
Ref operator*() { return _pnode->_val; }
Ptr operator->() { return _pnode; }
Self &operator++() {
_pnode = _pnode->_next;
return *this;
}
Self &operator--() {
_pnode = _pnode->_prev;
return *this;
}
bool operator!=(const Self &other) { return _pnode != other._pnode; }
};
迭代器区间的构造
template<class InputIterator>
list(InputIterator first, InputIterator last) {
initialize();
while (first != last) {
pushBack(*first);
++first;
}
}
测试
//
// main.cpp
// list
//
// Created by Bacon on 2023/4/13.
//
#include <iostream>
using namespace std;
#include "list.h"
void t1() {
bacon::list<int> lt;
lt.pushBack(1);
lt.pushBack(2);
lt.pushBack(3);
lt.pushBack(4);
lt.print();
lt.popBack();
lt.print();
lt.popBack();
lt.print();
lt.popBack();
lt.print();
}
void t2() {
bacon::list<int> lt;
lt.pushBack(1);
lt.pushBack(2);
lt.pushBack(3);
lt.pushBack(4);
lt.print();
bacon::list<int>::iterator it = lt.begin();
while(it != lt.end()) {
cout << *it << " ";
++it;
}
cout << endl;
for(auto& e : lt) cout << e << " ";
cout << endl;
}
void t3() {
bacon::list<int> lt;
lt.pushBack(1);
lt.pushBack(2);
lt.pushBack(3);
lt.pushBack(4);
lt.print();
bacon::list<int>::iterator pos = lt.find(3);
lt.insert(pos, 999);
lt.insert(lt.begin(), 111);
lt.insert(lt.end(), 333);
lt.print();
pos = lt.find(999);
lt.erase(pos);
pos = lt.find(111);
lt.erase(pos);
pos = lt.find(333);
lt.erase(pos);
lt.print();
}
void t4() {
bacon::list<int> lt;
lt.pushBack(1);
lt.pushBack(2);
lt.pushBack(3);
lt.pushBack(4);
lt.print();
bacon::list<int> lt1(lt);
lt1.print();
bacon::list<int> lt2;
lt2 = lt1;
lt2.print();
}
int main(int argc, const char * argv[]) {
t1();
cout << "–––––––––––––––––––––––––––-" << endl;
t2();
cout << "–––––––––––––––––––––––––––-" << endl;
t3();
cout << "–––––––––––––––––––––––––––-" << endl;
t4();
return 0;
}
1 2 3 4
1 2 3 4
1 2 3 4
–––––––––––––––––––––––––––-
1 2 3 4
111 1 2 999 3 4 333
1 2 3 4
–––––––––––––––––––––––––––-
1 2 3 4
1 2 3 4
1 2 3 4
vector vs list
vector
优点:文章来源:https://www.toymoban.com/news/detail-427075.html
- 随机访问O(1)
- 尾部操作O(1)
- CPU依照局部性原理拿取一坨数据时,缓存命中率高
缺点:文章来源地址https://www.toymoban.com/news/detail-427075.html
- 其他位置操作O(n)
- 因连续存储,需要动态扩容,可能浪费空间
*局部性原理:计算机中,访问某个位置的数据,其周围的数据也很可能被访问到。
为什么扩2倍
合适。扩多了容易浪费,扩少了容易频繁,开销大。
list
优点:
- 任意位置操作O(1)
- 逻辑上顺序存储,但是物理上随机存储,所以不会浪费空间
缺点:
- 不支持随机访问,访问是O(n)
- 缓存命中率低
整体代码
//
// list.h
// list
//
// Created by Bacon on 2023/4/13.
//
#ifndef list_h
#define list_h
#include <iostream>
#include <cassert>
namespace bacon {
template<class T>
struct listNode {
T _val;
listNode *_next;
listNode *_prev;
listNode(const T &val)
: _val(val),
_next(nullptr),
_prev(nullptr) {}
};
template<class T, class Ref, class Ptr>
struct listIterator {
typedef listNode<T> node;
typedef listIterator<T, T &, T *> Self;
node *_pnode;
listIterator(node *pnode)
: _pnode(pnode) {
}
Ref operator*() { return _pnode->_val; }
Ptr operator->() { return _pnode; }
Self &operator++() {
_pnode = _pnode->_next;
return *this;
}
Self &operator--() {
_pnode = _pnode->_prev;
return *this;
}
bool operator!=(const Self &other) { return _pnode != other._pnode; }
};
// template<class T>
// struct listConstIterator {
// typedef listNode<T> node;
// const node *_pnode;
//
// listConstIterator(node *pnode)
// : _pnode(pnode) {
// }
//
// const T &operator*() { return _pnode->_val; }
//
// const T *operator->() { return _pnode; }
//
// listConstIterator operator++() {
// _pnode = _pnode->_next;
// return *this;
// }
//
// listConstIterator operator--() {
// _pnode = _pnode->_prev;
// return *this;
// }
//
// bool operator!=(const listConstIterator<T> &other) { return _pnode != other._pnode; }
// };
template<class T>
class list {
public:
typedef listNode<T> node;
typedef listIterator<T, T &, T *> iterator;
typedef listIterator<T, const T &, const T *> constIterator;
list() { initialize(); }
~list() {
node *cur = _dummyHead;
while (cur != _dummyHead) {
node *temp = cur;
cur = cur->_next;
delete temp;
}
_dummyHead = nullptr;
}
template<class InputIterator>
list(InputIterator first, InputIterator last) {
initialize();
while (first != last) {
pushBack(*first);
++first;
}
}
list(list<T> &other) {
initialize();
list<T> tmp(other.begin(), other.end());
swap(tmp);
}
list &operator=(list<T> &other) {
swap(other);
return *this;
}
public:
iterator begin() { return iterator(_dummyHead->_next); }
iterator end() { return iterator(_dummyHead); }
constIterator begin() const { return constIterator(_dummyHead->_next); }
constIterator end() const { return constIterator(_dummyHead); }
void pushBack(const T &val) {
node *newNode = new node(val);
node *tail = _dummyHead->_prev;
newNode->_next = _dummyHead;
_dummyHead->_prev = newNode;
tail->_next = newNode;
newNode->_prev = tail;
}
void popBack() {
assert(!empty());
node *tail = _dummyHead->_prev;
node *tailPrev = tail->_prev;
tailPrev->_next = _dummyHead;
_dummyHead->_prev = tailPrev;
delete tail;
}
void pushFront(const T &val) {
insert(begin(), val);
}
iterator insert(iterator posIterator, const T &val) {
node *posPrev = posIterator._pnode->_prev;
node *pos = posIterator._pnode;
node *newNode = new node(val);
newNode->_next = pos;
pos->_prev = newNode;
posPrev->_next = newNode;
newNode->_prev = posPrev;
return iterator(newNode);
}
iterator erase(iterator posIterator) {
assert(!empty() && posIterator != end());
node *posPrev = posIterator._pnode->_prev;
node *posNext = posIterator._pnode->_next;
posPrev->_next = posNext;
posNext->_prev = posPrev;
delete posIterator._pnode;
return iterator(posNext);
}
void print() {
node *cur = _dummyHead->_next;
while (cur != _dummyHead) {
cout << cur->_val << " ";
cur = cur->_next;
}
cout << endl;
}
iterator find(const T &val) {
node *cur = _dummyHead->_next;
while (cur != _dummyHead) {
if (cur->_val == val) return iterator(cur);
cur = cur->_next;
}
return end();
}
bool empty() { return _dummyHead->_next == _dummyHead; }
void swap(list<T> &other) {
std::swap(_dummyHead, other._dummyHead);
}
private:
void initialize() {
_dummyHead = new node(0);
_dummyHead->_next = _dummyHead->_prev = _dummyHead;
}
private:
listNode<T> *_dummyHead;
};
}
#endif /* list_h */
到了这里,关于【C++初阶9-list实现】封装——我主沉浮,何不能至?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!