深拷贝 浅拷贝 以及赋值运算符= 的重载

来源:互联网 发布:java poi打印word文档 编辑:程序博客网 时间:2024/06/06 09:18

http://blog.csdn.net/sxhelijian/article/details/23209967

类中带指针就要深拷贝,否则浅拷贝即可.


在一些程序当中,需要将一个对象里的值直接传递给另一个对象,需要进行对象的拷贝。但是在某些情况下也会出现错误,如类内部成员需要动态开辟内存,实行位拷贝,就是将一个对象的值完全复制给另一个对象,如A=B,但如果B中也有一个指针申请了内存,那么A中的成员变量也指向了同一块内存,如果当释放资源的时候,就会导致野指针的出现,出现错误。

深拷贝的简单理解 就是在复制对象内容的过程中,需要重新分配资源,而浅拷贝则是不要重新分配资源。但是浅拷贝存在着问题:当资源释放时会产生资源归属不清的问题,导致运行错误。

当自己书写拷贝函数的时候,类中本身默认的拷贝函数已经不存在,此时需要在拷贝函数处理所需处理的所有数据。

拷贝和赋值:

当对象在声明的时候即用另一个对像进行初始化 即称为拷贝。如果是在声明之后,在进行赋值操作,则需要重载赋值操作符。

结合代码:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. #include <iostream>  
  2. #include <cstring>  
  3. using namespace std;  
  4.   
  5. class A{  
  6.     private:  
  7.         char* data;  
  8.     public:  
  9.         A(){}  
  10.         A(char* num){  
  11.             int len = strlen(num)+1;  
  12.             data = new char[len];  
  13.             strcpy(data,num);  
  14.             cout<<data<<endl;  
  15.         }  
  16.   
  17.         ~A(){  
  18.             delete[] data;  
  19.         }  
  20.   
  21.         void show(){  
  22.             cout<<data<<endl;  
  23.         }  
  24. };  
  25.   
  26. int main(){  
  27.     A a("hahah");  
  28.     A b;  
  29.     b = a;  
  30.     b.show();  
  31.     A c(a);  
  32.     c.show();  
  33.     return 0;  
  34. }  

默认在缺省状态下的赋值操作符 执行 b=a 内存分析:


A,B 指向了同一个内存地址,此时不会出现问题,但是在析构的同时,会出现俩个指针同时释放相同的内存地址,则会出现错误。

需要通过重载赋值运算符进行处理:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. #include <iostream>  
  2. #include <cstring>  
  3. using namespace std;  
  4.   
  5. class A{  
  6.     private:  
  7.         char* data;  
  8.   
  9.     public:  
  10.         A(){}  
  11.         A(char* num){  
  12.             int len = strlen(num)+1;  
  13.             data = new char[len];  
  14.             strcpy(data,num);  
  15.             cout<<data<<endl;  
  16.         }  
  17.   
  18.         //浅拷贝  
  19.         //      A(const A& o):data(o.data){}  
  20.   
  21.         //拷贝构造函数  深拷贝  
  22.        A(const A& o){  
  23.            int len = strlen(o.data)+1;  
  24.            data = new char[len];  
  25.            if(data!=NULL)  
  26.                 strcpy(data,o.data);  
  27.         }  
  28.   
  29.         //赋值运算符的重载  
  30.   
  31.         A& operator=(const A& o){  
  32.             if(this == &o)  
  33.                 return *this;  
  34.             delete[] data;  
  35.             data = NULL;  
  36.   
  37.             data = new char[strlen(o.data)+1];  
  38.             strcpy(data,o.data);  
  39.             return *this;  
  40.         }  
  41.   
  42.         ~A(){  
  43.             delete[] data;  
  44.         }  
  45.   
  46.         void show(){  
  47.             cout<<data<<endl;  
  48.         }  
  49. };  
  50.   
  51. int main(){  
  52.     A a("hahah");  
  53.     A b;  
  54.     b = a;  
  55.     b.show();  
  56.     A c(a);  
  57.     c.show();  
  58.     return 0;  
  59. }  

需要的内存分析应该是这样的:


注:

1.不要在调用构造函数时向其传递对象,应该传递对象的引用。因为传递对象的时候,还是需要调用构造函数,之后向其传入的还是对象,又再一次的调用构造函数,...,如此循环继续下去,导致段错误(栈溢出)。

2.在进行拷贝构函数的时候 函数不需要返回值,但是在进行赋值操作符的时候,需要有返回值,void类型,类的本身,或者是类的引用。

然而 返回是void的时候,不支持链式的赋值即: a=b=c=d...

返回类型的是类的本身

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. A operator=(const A& o){  
  2.             if(this == &o)  
  3.                 return *this;  
  4.             delete[] data;  
  5.             data = NULL;  
  6.   
  7.             data = new char[strlen(o.data)+1];  
  8.             strcpy(data,o.data);  
  9.             return *this;  
  10.         }  

具体步骤是:
(1)释放元内存资源

(2)申请一块新的内存空间

(3)原堆内存的值赋值到新的内存

(4)创建临时对象(调用拷贝构造函数),返回临时对象

(5)临时对像结束,调用析构函数,释放对象内存

返回类型如果是对象的引用:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. A& operator=(const A& o){  
  2.             if(this == &o)  
  3.                 return *this;  
  4.             delete[] data;  
  5.             data = NULL;  
  6.   
  7.             data = new char[strlen(o.data)+1];  
  8.             strcpy(data,o.data);  
  9.             return *this;  
  10.         }  

具体步骤:

(1)释放原内存资源

(2)申请新的内存资源

(3)原值赋值到新的内存

(4)结束

返回对象的引用 不需要调用拷贝构造函数,提高效率,建议采纳。

但是在重载赋值函数中,出现显示地用delete释放自身data的内存。同时我们也会在析构函数中用delete释放自身data的内存。如果这个类型中添加新的指针成员变量,那么我们至少需要做两处修改,即同时在析构函数和这个赋值运算符函数里添加一条delete语句来释放新指针所指向的内存。一个改动需要在代码中多个地方修改代码,通常是有安全隐患的。通常我们会记得在析构函数里用delete释放指针成员变量,但未必每次都记得到赋值运算符函数来添加代码释放内存.

基于以上,在函数中设置一个变量,用于存储,而后不需要二次释放内存。

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. A& operator=(const A& str){  
  2.             if(this!=&str){  
  3.             A a(str);  
  4.             char* temp = str.data;  
  5.             str.data = data;  
  6.             data = temp;  
  7.             }  
  8.         return *this;  
  9.         }  

在这个函数中,我们定义一个临时实例a,并把a的data指向当前实例(*this)的data。由于a是个局部变量,但程序员运行到if的外面是也就出了的该变量的域,就会自动调用a的析构函数,就会把a.data所指向的内存释放掉。由于a.data指向的内存就是当前实例之前data的内存。这就相当于自动调用析构函数释放当前实例的内存。如果新增加指针成员变量,我们只需要在析构函数里正确地释放,而不需要对赋值运算符函数做任何修改,这样操作更加高效


http://blog.csdn.net/xd_122/article/details/25624477?utm_source=tuicool&utm_medium=referral

0 0