C++中的浅拷贝与深拷贝

来源:互联网 发布:淘宝扩容128g u盘判断 编辑:程序博客网 时间:2024/06/15 10:28

用自定义的String类解释什么是浅拷贝什么是深拷贝。

class String { private:  char* _str; };
浅拷贝是在调用拷贝函数时进行了值拷贝,这样的拷贝看似没有问题,在调用析构函数时会导致内存泄漏,系统奔溃。

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>using namespace std;class String{public:String(const char*str=""):_str(new char[strlen(str) + 1]){strcpy(_str, str);cout << "String" << endl;}~String(){if (_str){delete[]_str;cout << "~String" << endl;}}char *Getstr(){return _str;}private:char*_str;};void TestString(){String s1("hello world!");String s2;String s3(s1);//String s2 = s1;cout << s1.Getstr() << endl;cout << s3.Getstr() << endl;}int main(){TestString();system("pause");return 0;}
我们自己编写了构造函数和析构函数,在运行测试用例时出现系统奔溃:

是因为在String s3(s1)时系统调用默认拷贝构造即“浅拷贝”。


所以我们要自己编写拷贝构造与赋值运算符重载,进行深拷贝。

深拷贝是指在拷贝构造时,新开辟一块空间,将_str指向的内容拷贝到这块新的空间里,再将自己的_str指向这块空间。下面是代码:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>using namespace std;class String{public:String(const char*str=""):_str(new char[strlen(str) + 1]){strcpy(_str, str);cout << "String" << endl;}~String(){if (_str){delete[]_str;cout << "~String" << endl;}}String(const String& d){this->_str = new char[strlen(d._str) + 1];   //this可以省略,+1是因为字符串后还有‘\0’strcpy(_str, d._str);}String& operator=(const String& d){if (this != &d){delete[]_str;          //首先释放自己空间_str = new char[strlen(d._str) + 1];strcpy(_str, d._str);}return *this;}char *Getstr(){return _str;}private:char*_str;};void TestString(){String s1("hello world!");String s2;String s3(s1);    s2 = s1;cout << s1.Getstr() << endl;cout << s3.Getstr() << endl;cout << s2.Getstr() << endl;}

运行结果为:


深拷贝有两种写法,第一种是传统写法,就是我上面这种写法,老老实实开辟新空间,老老实实拷贝字符串;

还有一种是现代写法,相对于传统写法更简洁一点,是让别人开空间拷贝字符串,再将别人的空间与自己交换,坐享渔翁之利。

代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>using namespace std;class String{public:String(const char*str=""):_str(new char[strlen(str) + 1]){strcpy(_str, str);cout << "String" << endl;}~String(){if (_str){delete[]_str;cout << "~String" << endl;}}String(const String& d):_str(NULL)               //如果tmp置空可以避免释放野指针        {String tmp(d._str);        //让临时对象tmp去构造与d._str相同的空间,再将tmp与自己交换swap(_str, tmp._str);}String& operator=(const String& d){if (this != &d){String tmp(d._str);       //赋值首先要做的是释放自己的空间,然后创建一块新空间,赋值为其他对象成员变量swap(_str, tmp._str);     //现代写法可以减少释放自己空间这一步骤,因为临时对象tmp在函数调用后自动释放}return *this;}char *Getstr(){return _str;}private:char*_str;};void TestString(){String s1("hello world!");String s2;String s3(s1);    s2 = s1;cout << s1.Getstr() << endl;cout << s3.Getstr() << endl;cout << s2.Getstr() << endl;}int main(){TestString();system("pause");return 0;}
现代写法中的赋值运算符重载也可以使用对象参数,上面使用的是引用。

String& operator=(String s){swap(_str, s._str);return *this;}
在这种写法中,因为参数是一个临时变量,所以在调用赋值函数时String s就是对象的临时拷贝,可以直接交换,

String s因为是临时变量,在出了作用域后自动销毁,不用自己调用delete函数。










原创粉丝点击