为什么要使用拷贝构造?

来源:互联网 发布:淘宝分流比例设置 编辑:程序博客网 时间:2024/06/06 20:41

先构造这样的代码:

static int objectCount = 0;class HowMany {public:    HowMany(){objectCount++;print("HowMany()");}    //HowMany(const HowMany& o){objectCount++;print("HowMany(const HowMany&)");}    HowMany(int i){objectCount++;print("HowMany(int i)");}    void print(const string& msg=""){        if(msg.size()!=0) cout<<msg<<":";        cout<<"objectCount = "<<objectCount<<endl;    }    ~HowMany(){        objectCount--;        print("~HowMany()");    }};HowMany fun(HowMany x){    cout<<"begin of fun "<<endl;    cout<<"x argument inside fun() "<<"objectCount = "<<objectCount<<endl;    cout<<"end of fun()"<<endl;    return x;}int main(){    HowMany h;    h.print("after construction of h");    HowMany h2 = fun(h);    h.print("after call to fun()");    return 0;}

如果我们将HowMany(const HowMany& o)函数注释掉,我们会得到如下的结果:

    HowMany():objectCount = 1    after construction of h:objectCount = 1    begin of fun     x argument inside fun() objectCount = 1    end of fun()    ~HowMany():objectCount = 0    ~HowMany():objectCount = -1    after call to fun():objectCount = -1    ~HowMany():objectCount = -2    ~HowMany():objectCount = -3

分析:得到这样的结果的原因时:在fun(h)函数中,重新创建了一个HowMany对象,而该对象却的创建没有经过构造函数,在将fun(h)返回的对象x赋值给h2时,也没有经过构造函数,从而导致了在这两个本来该计数的地方没有计数,但是在析构这两个对象的时候,这两个对象的析构函数却发生了作用,即objectCount–,最后导致了之一结果。第一个析构函数是析构的x,第二个析构函数析构的是x在返回时赋给的临时变量对象,后面两个析构的是h和h2。
我们找到了发生这样结果的原因,即:在HowMany h2 = h这样的操作时,没有经过构造函数。事实上,这样的赋值操作也可以写成这样:HowMany h2(h),而实际上,在C++中,这样的操作被称为拷贝构造。发生拷贝构造时,调用的是拷贝构造函数,如果用户没有为类定义拷贝构造函数,则系统会为之默认生成一个拷贝构造函数,而该默认的拷贝构造的函数体中,没有别的操作。一般我们在定义一个类时,除了定义构造函数之外,还需要定义一个拷贝构造函数。而拷贝构造函数的必须以以下形式定义,在本类中:HowMany(const HowMany &),原因是在传入一个本类的对象时,如果不用const加引用,则会又发生调用默认的拷贝构造函数,因为拷贝构造函数被默认规定必须是const &型。加上这样的拷贝构造函数之后,就会得到如下正确结果:

HowMany():objectCount = 1    after construction of h:objectCount = 1    HowMany(const HowMany&):objectCount = 2    HowMany(const HowMany&):objectCount = 3    begin of fun     x argument inside fun() objectCount = 3    end of fun()    HowMany(const HowMany&):objectCount = 4    ~HowMany():objectCount = 3    ~HowMany():objectCount = 2    after call to fun():objectCount = 2    ~HowMany():objectCount = 1    ~HowMany():objectCount = 0

definition和assignment
在这里,需要注意的是:definition和assignment,即定义和赋值。定义的时候就初始化对象时,调用的是构造函数来处理初始化类容的,即HowMany h2=h等价于HowMany h2(h);而在对象已经定义过了,再来赋值时,就是使用的运算符重载了。
在函数返回对象时的两种可能情况
在fun函数返回对象时,是否会先生成一个临时对象@tmp = x,然后再 HowMany h2 = @tmp,还是HowMany h2=x,这取决于你的编译器。
为什么要使用自己定义的拷贝构造函数?
在C++中,通常在定义一个类时,一定要定义如下三个函数:构造函数、virtual的析构函数、拷贝构造函数。前两者毋庸置疑(virtual析构方式向上造型时析构基类对象而没有析构实际的对象)。定义自己的拷贝构造函数而不是用默认的构造函数是因为在默认的拷贝构造函数中,是直接进行赋值的。当遇到类中含有指针或引用成员时就会发生两个指针或引用指向同一块内存中的情况(如果输出两个指针,他们的内容相同),从而在delete掉一个指针或引用成员时,另一个对象所指向的东西也被delete掉。如:

        Public:~HowMany(){delete []name;}        Private:char *name; 

如果没有定义拷贝构造函数,在HowMany h2(h)中会发生指针的赋值(使用了默认构造函数),在结束时,会两个对象都会调用自己的析构函数,但是这样的代码会出错。因为指针指向了同一个name,在析构一个对象时,就已经将namedelete掉了,再析构另一个对象时,再次delete就会出现找不到name,所以会出错。由此可见,类的拷贝构造函数必须自己定义。
在什么样的情况下会调用拷贝构造函数?
调用拷贝构造函数一定是发生在definition中的,而不是发生在assignment中。由此可见,函数的参数是对象本身而非指针或引用时,会发生definition,在普通的定义就初始化时也会发生definition,其他类似情况也会有调用拷贝构造函数。但是在assignment中,调用的不再是拷贝构造函数,而是重载过的操作符或默认的操作符赋值。