雕刻时光

just do it……nothing impossible
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

拷贝构造函数中的陷阱

Posted on 2013-12-25 16:28  huhuuu  阅读(3498)  评论(3编辑  收藏  举报

  拷贝构造函数大家都比较熟悉,通俗讲就是传入一个对象,拷贝一份副本。

  不过看似简单的东西,实际不注意的话就会产生问题!

#include<iostream>
using namespace std;

class CExample 
{
public:
    int a,b,c;
    char *str;

public:
    //构造函数
    CExample(int tb)
    { 
        a = tb;
        b = tb+1;
        c = tb+2;
        str=(char *)malloc(sizeof(char)*10);
        strcpy(str,"123456789");
        cout<<"creat: "<<endl;
    }


    //析构函数
    ~CExample()
    {
        cout<< "delete: "<<endl;
    }

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

    //拷贝构造

    //CExample(const CExample& C)
    //{
    //    str=(char *)malloc(sizeof(char)*10);
    //    strcpy(str,C.str);
    //    cout<<"copy"<<endl;
    //}
};

//全局函数,传入的是对象
void g_Fun(CExample C)
{
    C.a=0;C.b=0;C.b=0;
    strcpy(C.str,"aaabbbccc");
    cout<<"test"<<endl;
}

int main()
{
    CExample test(1);
    cout<<"str:"<<test.str<<" a="<<test.a<<" b="<<test.b<<" c="<<test.c<<endl;
    
    g_Fun(test);//传入对象

    cout<<"str:"<<test.str<<" a="<<test.a<<" b="<<test.b<<" c="<<test.c<<endl;
    getchar();
    return 0;
}

这个结果似乎出乎了我们的预料,作为形式参数 test对象被修改了,同时是test.str的部分被修改了,test的整数成员变量没有被修改!

咱们先了解一下系统默认的拷贝构造函数,因为类中没有写自己的拷贝构造函数,所以调用的是默认的拷贝构造函数。

Thinking in c++:对于简单结构,编译器会自动生成一个缺省的,就是位拷贝(bitcopy)。
对于比较复杂的类型,编译器就会自动生成一个缺省的拷贝构造函数。

class CExample 
{
    int a,b,c;
};

这就是一个简单结构的类,位拷贝,就是按对象在内存中的二进制进行拷贝,对于不涉及指针等类型的时候,位拷贝是比较不错的拷贝方法。

但是,要是一个类中有指针类型的时候,如

class CExample 
{
    int a,b,c;
    char *str;
};

位拷贝就会把指针地址拷贝了一下,话句话说,这里只进行了“浅拷贝”,一旦副本里涉及到指针的操作,必然就会影响到原始对象的成员变量,这就是导致,上面代码中对象的整数变量没被修改(对整数变量的位拷贝其实就是一种“深拷贝”),而str所指的对象被修改的原因。

那么该如何防止对副本的修改影响原始对象呢?

答案是用户自定义拷贝构造函数!

    CExample(const CExample& C)
    {
        a=C.a;b=C.b;c=C.b;
        str=(char *)malloc(sizeof(char)*10);
        strcpy(str,C.str);
        cout<<"copy"<<endl;
    }

 

这样就可以正确完成拷贝构造的操作了。

总结:对于简单的数据类型,可以使用系统默认的拷贝构造函数;但对于复杂的数据类型(如指针),其实就是深拷贝和浅拷贝的区别!一般类如果包含指针或引用成员,应该遵守Rule of Three原则。(感谢@24K纯开源

 

@24K纯开源 指出的三法则:

三法则英语rule of threethe Law of The Big ThreeThe Big Three三法则三大定律)在 C++ 程序设计里,它是一个以设计的基本原则而制定的定律,三法则的要求在于,假如类型有明显地定义下列其中一个成员函数,那么程序员必须连其他二个成员函数也一同编写至类型内,亦即下列三个成员函数缺一不可。 [1]:

参考 http://zh.wikipedia.org/wiki/%E4%B8%89%E6%B3%95%E5%89%87_(C%2B%2B%E7%A8%8B%E5%BC%8F%E8%A8%AD%E8%A8%88)