C++拷贝构造函数——深拷贝/浅拷贝

基本概念:

深拷贝:

  将一个对象拷贝给另一个对象的时候,被赋值的对象存储赋值对象的一个额外副本。若类成员中含有指针成员,且用new初始化的时候,被赋值的成员,会申请一块内存,将赋值对象的指针成员所指的内存的内容复制到这块内存中。两个指针各自指向自己申请的内存

 

浅拷贝:

  和深拷贝相似,浅拷贝对于非指针成员都是直接赋值。但是当类成员中含指针成员,且用new初始化的时候,被赋值的成员指针并不会额外申请一块内存,而仅仅是将自己指向赋值对象的指针成员所指的那块内存。两个指针指向同一块内存

 

 当我们没有定义类的复制构造函数和赋值运算符时,编译器会生成默认的版本,它们使用浅拷贝

回到上面所说的,为什么我们需要定义"深拷贝"的复制构造函数和赋值运算法捏 ? 难道,是因为默认的浅拷贝会导致错误 ?

没错!  我们知道,如果定义的类中含指针成员,如果它将会使用new申请新内存。在析构函数中,我们会用delete释放相应的内存占用。


考虑两种情况:

 

1. 一个对象使用另外一个已有对象初始化,这样将调用默认复制构造函数(有可能还会调用赋值操作符,视编译器而定)。由于使用浅拷贝,就会存在这两个对象的指针成员指向同一块内存的情况,当这两个对象弃用时,会调用它们的析构函数。这样会出现同一块内存被释放两次的情况,出现未知的错误。

类似地,如果你定义了一个返回对象的函数,也会造成同一块内存释放两次的情况,为啥 ? 因为这还将调用复制构造函数,按值传递意味着创建原始变量的一个副本。caller和这个函数(callee)中的对象的指针指向同一块内存。当函数返回的时候,函数中的这个对象要被kill掉,调用析构函数了,释放掉占用的内存... 放心,这些都不会告诉你的。嗯,当caller中的那个对象析构时,那块内存又被释放了一次... 仍然是不可预知的错误。类似地,创建临时对象的时候,也会调用复制构造函数,这将发生同样的趣事--同样的奇怪的错误。

 

2. 两个已有对象之间的赋值,这将调用默认赋值运算符函数。后面的情况和1相同,都是浅拷贝闹的--两个对象的指针指向同一块内存,然后被释放两次。

 

当定义的类中含有指针成员,且使用常规new(或定位new,定位在申请的堆内存中)初始化的时候,需定义深拷贝的复制构造函数和赋值操作符”,不然会被外星人抓走。

图示:



代码示例:

 C++ Code //浅拷贝 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include<iostream>
#include<cstring>
using  namespace std;
class Person
{
     char *pName;
public:
    Person( char *pN)
    {
        cout <<  "Constructing "
             << pN << endl;
        pName =  new  char[strlen(pN) +  1];
         if(pName !=  0)
            strcpy(pName, pN);
    }

    ~Person()
    {
        cout <<  "Destructing "
             << pName << endl;
         if(pName)    delete []pName;
        pName =  NULL;
    }
};

int main()
{
    Person p1( "Tom");
    Person p2(p1);
}
 C++ Code  //深拷贝
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include<iostream>
#include<cstring>
using  namespace std;
class Person
{
     char *pName;
public:
    Person( char *pN);
    Person( const Person &p);
    ~Person();
};
Person::Person( char *pN)
{
    cout <<  "Constructing "
         << pN << endl;
    pName =  new  char[strlen(pN) +  1];
     if(pName !=  0)  strcpy(pName, pN);
}
Person::Person( const Person &p)
{
    cout <<  "Copying " << p.pName <<  " \n";
    pName =  new  char[strlen(p.pName) +  1];
     if(pName !=  0)  strcpy(pName, p.pName);
}

Person::~Person()
{
    cout <<  "Destructing "
         << pName << endl;
     if(pName)   delete []pName;
    pName =  NULL;
}
void main()
{
    Person p1( "Tom");
    Person p2(p1);
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值