这篇呢是关于深拷贝的拓展知识。目前我们学到的知识中,会用到深拷贝就是拷贝构造和赋值运算符重载。传统写法的代码实现相信大家已经是手拿把掐,知道现代写法如何实现吗?我们来一起看看。(全文以我们前面模拟实现的string类为背景,文中函数的返回值类型最好是string的引用)
1.拷贝构造和operator=的传统写法
用已经存在的对象去初始化另一个刚创建的对象,此时调用拷贝构造。
string s1("hello world");string s2 = s1;//此时调用拷贝构造//s2(s1)和s2 = s1一个意思,写法不同而已
string(const string& s)//拷贝构造传统写法{_str = new char[s._capacity + 1]; //s2开和s1一样的大小strcpy(_str, s._str);//拷贝数据_size = s._size;_capacity = s._capacity;}
两个已经存在的对象之间的拷贝复制,就调用operator=赋值运算符重载。
string s1("hello world");string s2("xxxxxxxx");s2 = s1;//s1,s2都已经存在,此时是赋值
string operator=(const string& s) //传统赋值重载{if (this != &s) //不是自己给自己赋值时{delete[] _str; //释放原来空间_str = new char[s._capacity + 1];_size = s._size;_capacity = s._capacity;}return *this;}
传统写法呢就是我们自己开空间,自己拷贝数据,现代写法就是我们让“别人”帮我们开空间,帮我们拷贝数据。
2.拷贝构造现代写法
先看代码再解释。这里s就是s1,隐形的this指针就是s2。
//拷贝构造s2(s1)string(const string& s)//拷贝构造现代写法{string temp(s._str);swap(_str, temp._str);swap(_size, temp._size);swap(_capacity, temp._capacity);}
s2想和s1有一样大的空间,一样的值,我们不自己处理这些事,让temp来处理,temp处理好了,s2和temp一交换。
这样处理的前提是,我们在成员函数声明的地方给了缺省值,不然会对随机值释放,如下。
private:char* _str = nullptr;size_t _size = 0;size_t _capacity = 0;
是不可以对随机值释放的,相当于释放的野指针,这里要注意一下。
这里的交换呢,我们还可以单独写一个交换函数。
void swap(string& s){//调用算法库里面的swap函数std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}
然后再调用这个自己写的swap函数。
//s2(s1)string(const string& s)//拷贝构造现代写法{string temp(s._str);swap(temp);}
看到这里我相信你一定有很多疑惑,不着急,存在即合理,我慢慢解释,来画图分析。
开始的时候s2是指向nullptr,因为我们前面给的缺省值是nullptr。
然后我们构造了一个temp对象,temp里面存和s1一样的内容。
此时temp里面的内容就是s2想要的,this就是s2,然后s2和temp交换。
temp出了作用域就销毁了,此时temp是nullptr,这里会调用析构函数,可以释放空,所以前面我们才强调要给缺省值。
此时s2就和s1一样大,而且内容也一样。但是这个活是“别人”干的,是temp干的,然后s2通过交换,掠夺“别人”的“成果”。
3.operator=的现代写法
赋值也可以用和拷贝构造类似的写法。
//赋值s2 = s1;string operator=(const string& s)//赋值重载现代写法{if (this != &s) //不是自己给自己赋值时{string temp(s._str);swap(temp);}return *this;}
s2想和s1有一样大的空间,一样的值,还是一样,我们不自己处理这些事,让temp来处理。
我们构造了一个temp对象,temp里面存和s1一样的内容。
此时temp里面的内容就是s2想要的,this就是s2,然后s2和temp交换。
temp出了作用域就销毁了,此时temp是"xxxxxxxx",这里会调用析构函数。
最后返回*this,就是"hello world"。此时s2就和s1一样大,而且内容也一样。
从图的分析可以看出,我们不仅掠夺了temp的成果,还让temp把s2原来的空间给释放了,在传统写法中我们首先要释放s2的旧空间,现在我们都不用自己释放了。
传统写法就是我们自己完成所有步骤,现代写法就是让“别人”帮我们完成。
4.operator=现代写法的再变形
//赋值重载s2 = s1;string operator=(string s){swap(s);return *this;}
参数列表不再引用传参,因为这个代码下引用传参会改变s1。
this直接和s交换。
然后返回*this,出作用域s销毁。
传统写法和现代写法你更喜欢哪个呢?我们以后也会用到现代写法的,就介绍到这里了,拜拜~