(转) 深浅拷贝问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(转) 深浅拷贝问题相关的知识,希望对你有一定的参考价值。

转自:http://blog.csdn.net/lwbeyond/article/details/6202256/

1. 

浅拷贝

    所谓浅拷贝,指的是在对象复制时,只对对象中的数据成员进行简单的赋值,默认拷贝构造函数执行的也是浅拷贝。大多情况下“浅拷贝”已经能很好地工作了,但是一旦对象存在了动态成员,那么浅拷贝就会出问题了,让我们考虑如下一段代码:

class Rect
{
public:
    Rect()      // 构造函数,p指向堆中分配的一空间
    {
        p = new int(100);
    }
    ~Rect()     // 析构函数,释放动态分配的空间
    {
        if(p != NULL)
        {
            delete p;
        }
    }
private:
    int width;
    int height;
    int *p;     // 一指针成员
};

int main()
{
    Rect rect1;
    Rect rect2(rect1);   // 复制对象
    return 0;
}

 在这段代码运行结束之前,会出现一个运行错误。原因就在于在进行对象复制时,对于动态分配的内容没有进行正确的操作。我们来分析一下:

    在运行定义rect1对象后,由于在构造函数中有一个动态分配的语句,因此执行后的内存情况大致如下:

 技术分享

 

    在使用rect1复制rect2时,由于执行的是浅拷贝,只是将成员的值进行赋值,这时 rect1.p= rect2.p,也即这两个指针指向了堆里的同一个空间,如下图所示:

技术分享

 

当然,这不是我们所期望的结果,在销毁对象时,两个对象的析构函数将对同一个内存空间释放两次,这就是错误出现的原因。我们需要的不是两个p有相同的值,而是两个p指向的空间有相同的值,解决办法就是使用“深拷贝”。

 

2. 深拷贝

    在“深拷贝”的情况下,对于对象中动态成员,就不能仅仅简单地赋值了,而应该重新动态分配空间,如上面的例子就应该按照如下的方式进行处理:

class Rect
{
public:
    Rect()      // 构造函数,p指向堆中分配的一空间
    {
        p = new int(100);
    }
    Rect(const Rect& r)
    {
        width = r.width;
        height = r.height;
        p = new int;    // 为新对象重新动态分配空间
        *p = *(r.p);
    }
    ~Rect()     // 析构函数,释放动态分配的空间
    {
        if(p != NULL)
        {
            delete p;
        }
    }
private:
    int width;
    int height;
    int *p;     // 一指针成员
};

此时,在完成对象的复制后,内存的一个大致情况如下:

 

技术分享

此时rect1的p和rect2的p各自指向一段内存空间,但它们指向的空间具有相同的内容,这就是所谓的“深拷贝”。

 

3. 

 防止默认拷贝发生

    通过对对象复制的分析,我们发现对象的复制大多在进行“值传递”时发生,这里有一个小技巧可以防止按值传递——声明一个私有拷贝构造函数。甚至不必去定义这个拷贝构造函数,这样因为拷贝构造函数是私有的,如果用户试图按值传递或函数返回该类对象,将得到一个编译错误,从而可以避免按值传递或返回对象。

// 防止按值传递
class CExample 
{
private:
    int a;

public:
    //构造函数
    CExample(int b)
    { 
        a = b;
        cout<<"creat: "<<a<<endl;
    }

private:
    //拷贝构造,只是声明
    CExample(const CExample& C);

public:
    ~CExample()
    {
        cout<< "delete: "<<a<<endl;
    }

    void Show ()
    {
        cout<<a<<endl;
    }
};

//全局函数
void g_Fun(CExample C)
{
    cout<<"test"<<endl;
}

int main()
{
    CExample test(1);
    //g_Fun(test); 按值传递将出错
    
    return 0;
} 

 

以上是关于(转) 深浅拷贝问题的主要内容,如果未能解决你的问题,请参考以下文章

深浅拷贝以及集合

深浅拷贝

深浅拷贝

C++中的深浅拷贝问题

深浅拷贝——string

深浅拷贝