深浅拷贝
谈到深浅拷贝这个问题,我们首先要知道为什么会有深浅拷贝、他们在什么样的场景下出现、一级怎么样去解决它带来的问题。
浅拷贝
例如在string容器中,定义string s的一个容器。
有:
构造函数——string s(“abc”)
拷贝构造——string s1(s);
赋值—— string s2;s2 = s;
在自己实现的string类中,在不自己手动实现拷贝构造的时候,系统会调用自己的拷贝构造。仅仅只是s2=s1,这样只是把s1的地址简单的拷贝了过去,而这里调用的就是我们俗称的浅拷贝。

这里我们可以看到s1、s2指向的是同一块空间,这时就会面临一个问题。当s1s2对象销毁的时候调用析构函数两个析构函数析构的是同一块空间这是系统不允许的,因为第一个函数析构把这块空间还给系统,在第二个对象析构之前系统可能把这块空间分配出去了s2再析构这块空间就不合理,程序会崩溃。

深拷贝
针对上述问题,要解决两个对象指向同一块地址空间,我们就要用到深拷贝的思想。
思路:我们在拷贝的时候,不能只是s2 = s1简单的把s1中的数据拿过来。我们要重新在堆上开辟一块空间,然后把s1所指内容拷贝过来这样两个对象所指的地址空间就不是一个。各自析构也不会出什么问题。
//深拷贝
string(const string& s)//这里相当于s2(s1),s是s1的别名string类型
:_str(new char[strlen(s._str) + 1])//开辟和原空间一样大的空间
{
strcpy(_str,s._str);//对内容进行拷贝
}
这时我们可以看出两个对象所指的空间地址不同。

深拷贝的现代写法
mystring(mystring& s)
:_str(nullptr)//swap在交换的时候_str是随机值,所以要初始化
{
mystring tem(s._str);
swap(_str,tem._str);
}
我们可以看出现代写法和传统写法是两种不同的思路,传统写法——开空间,拷数据。现代写法它的思路相当于“偷”。
因为拷贝构造必须引用传值,所以不能直接对S进行操作,所以创造一个临时的tem对象。利用构造函数把s对象中的字符串数据拷贝过去。最后this指针再把tem的数据抢过来,让自己指向数据的地址空间。
不过有的同学可能认为,这两串代码复杂程度差不多,但现代写法更加难以理解,有些得不偿失。其实对于拷贝构造两个方法确实差异不大,但对于赋值构造来说现代写法的优势就会体现出来。
赋值
赋值运算符的代码很好理解,开一块新的空间 释放掉旧的空间,然后让旧的空间的指针指向新开的空间。
mystring& operator=(mystring& s)
{
if (this = &s)//如果不是s=s这种情况。这里的&s是取s的地址
{
char* tem = new char[strlen(s._str) + 1];
strcpy(tem, s._str);
delete[] _str;
_str = tem;
}
return *this;
}
我们对比一下赋值的现代写法
(s1 = s2)
mystring& operator=(mystring s)
{
swap(_str, s._str);
return *this;
}
主函数一行就搞定了上面复杂的操作。
1、在传参的时候:之前上面的传参为了减少拷贝都是引用传参,而这里直接传,此时的s就是s2的拷贝,它是一个临时的对象 所以在结束的时候会销毁掉这这个临时对象。
2、s里面存的数据和s2是一模一样的,它里面的数据就是s1想要的。所以还是采用“抢”的思想。把s数据指针所指的东西和s1做了交换。同时把自己不用的空间给了S,让它在析构的时候顺便释放掉这块空间。
1725

被折叠的 条评论
为什么被折叠?



