- 定义拷贝赋值 :多利用公共操作,这里拷贝赋值的实现中选择利用定义好的拷贝构造一个rhs的副本,然后swap(必须是三次移动),利用这个副本是本地变量,离开作用域后自动析构的原因,节省了代码量。
- 类里访问对象的私有成员 :一个对象的私有成员在类外是不能通过对象用.运算符访问的,但是在类内例外,即使像拷贝赋值,依然可以使用形参对象的私有成员。
- 将迭代器设置为(内嵌)类 :(在这里我浅显的理解以及书中原作者的理解)当我们只是将指针当成迭代器的时候,解引用和++(–)等操作可能发生错误(iterator可能已经冲过终点),当将其声明成一个类的时候,我们便可以将其运算符的操作重载,加入检验的语句,并在错误时抛出异常。 当然,还有其他优点:更加符合oop思想、可复用性和模块化。
- 迭代器将表指针作为类成员 :为防止对象进行erase和insert的时候,迭代器是指向该对象的,需要检验,就是在每个迭代器初始化时,要初始化指向对象的指针,通过这个指针来检验。
- 返回类型不能都是引用 :在begin()、end()这样的返回类型为迭代器的函数里,我们用tail(head)初始化了一个迭代器对象,但这个是一个右值,我们不能将左值引用(返回类型)绑定到一个右值上。也不能在函数体里返回一个局部变量的引用。
- 派生类访问基类成员 :虽然派生类继承了基类(派生类对象可以访问任何从基类继承来的protected、private成员),但是在派生类内访问基类成员需要用类作用域符显示地访问基类成员,或者用this指针访问,不能直接访问(编译器会报错:因为基类成员并没有声明在派生类里)。
- 总结:1. 尽量多用公共操作。 2. 每个函数的形参设置成为什么类型(能不能声明为常量或引用),都要根据上传的实参(左值、右值)类型确定,否则就要进行函数重载。所以,每个API的功能要提前构思好。
#include<algorithm>
#include<algorithm>
#include<iostream>
template<typename object>
class list{
private:
struct node{
object data;
node * prev;
node * next;
node(const object & d = object{}, node * p = nullptr, node * n = nullptr)
:data(d), prev(p), next(n) {}
node(object && d, node * p = nullptr, node * n = nullptr)
:data(d), prev(p), next(n) {}
};
int theSize;
node * head;
node * tail;
void Ini(){
theSize = 0;
head = new node{};
tail = new node{};
head->next = tail;
tail->prev = head;
}
class const_iterator{
protected:
friend class list<object>;
node * cur;
const list<object> *theList;
const_iterator(node * p, const list<object> &lst):cur{p}, theList{lst} {}
public:
const_iterator():cur(nullptr) {}
const object & operator *() {
if(cur == theList->head || cur == nullptr || cur == theList->tail)
throw IteratorOutOfBoundsException{};
return cur->data;
}
const_iterator & operator -- () {
if(cur == theList->head || cur == nullptr)
throw IteratorOutOfBoundsException{};
cur = cur->prev;
return *this;
}
const_iterator & operator ++ () {
if(cur == nullptr || cur == theList->tail)
throw IteratorOutOfBoundsException{};
cur = cur->next;
return *this;
}
const_iterator operator ++ (int) {
if(cur == nullptr || cur == theList->tail)
throw IteratorOutOfBoundsException{};
const_iterator old = *this;
++(*this);
return old;
}
bool operator == (const const_iterator & rhs)const {
return cur == rhs.cur;
}
bool operator != (const const_iterator & rhs)const {
return !(*this == rhs);
}
};
class iterator : public const_iterator {
protected:
friend class list<object>;
iterator(node * p, const list<object> & lst): const_iterator{p, lst} {}
public:
iterator() = default;
object & operator * () {
if(const_iterator::cur == nullptr || const_iterator::cur == const_iterator::)
return const_iterator::cur->data;
}
const object & operator * ()const {
return const_iterator::cur->data;
}
iterator & operator ++ () {
this->cur = this->cur->next;
return *this;
}
iterator operator ++ (int) {
iterator old = *this;
++(*this);
return old;
}
iterator & operator -- () {
this->cur = this->cur->prev;
return *this;
}
};
public:
list(){
Ini();
}
~list(){
clear(); delete head; delete tail;
}
list(const list & rhs){
Ini();
for(auto & x : rhs) push_back(x);
}
list & operator = (const list & rhs){
list copy = rhs;
swap(*this, copy);
return *this;
}
list(list && rhs):theSize{rhs.theSize}, head{rhs.tail}, tail{rhs.tail} {
rhs.theSize = 0;
rhs.head = rhs.tail = nullptr;
}
list & operator = (list && rhs){
std::swap(theSize, rhs.theSize);
std::swap(head, rhs.head);
std::swap(tail, rhs.tail);
return *this;
}
void swap(list & lhs, list & rhs) {
list copy = std::move(lhs);
lhs = std::move(rhs);
rhs = std::move(copy);
}
iterator begin(){
return { head->next };
}
iterator end(){
return { tail };
}
const_iterator begin() const{
return { head->next };
}
const_iterator end() const{
return { tail };
}
int size() const {
return theSize;
}
int empty() const {
return theSize == 0;
}
void clear(){
while(!empty()) pop_front();
}
object & front(){
return *begin();
}
const object & front()const {
return *begin();
}
object & back() {
return *--end();
}
const object & back ()const {
return *--end();
}
void push_front(const object & x) {
insert(begin(), x);
}
void push_front(object && x) {
insert(begin(), std::move(x));
}
void push_back(const object & x) {
insert(end(), x);
}
void push_back(object && x) {
insert(end(), std::move(x));
}
void pop_front() {
erase(begin());
}
void pop_back() {
erase(--end());
}
iterator & insert(iterator itr, const object & x) {
if(this != itr.theList) throw IteratorMismatchException{ };
node * p = itr->cur;
++theSize;
return { p->prev = p->prev->next = new node(x, p->prev, p) };
}
iterator insert(iterator itr, object && x) {
node * p = itr.cur;
++theSize;
return { p->prev = p->prev->next = new node(std::move(x), p->prev, p) };
}
iterator erase(iterator itr) {
--theSize;
node * p = itr.cur;
node * ret{p->next};
p->prev->next = p->next;
p->next->prev = p->prev;
delete p;
return ret;
}
iterator erase(iterator from, iterator to) {
for(iterator itr = from; itr != to;) {
itr = erase(itr);
}
return to;
}
};