1. 用一个已存在对象初始化另一个新的对象;
2. 将一个对象作为参数按值调用方式传递给另一个对象时生成对象副本;
3. 生成一个临时对象作为函数的返回结果。
那么接着就看一下在这三种情况下拷贝构造函数分别在什么时候调用,以及如果有临时对象的话,在什么时候析构。先假定有一个类FOO,在构造函数中会输出“Constructing.”,在拷贝构造函数中会输出“Copy constructing.”,在析构函数中会输出“Destructing.”。输出结果中的注释是说明之用。
在声明语句中用一个对象初始化另一个对象
在声明语句中用一个对象初始化另一个对象时,不存在临时对象的问题,新的对象直接调用拷贝构造函数,进行对象的初始化,也没有什么调用顺序的问题了,例如:
- FOO obj1;
- FOO obj2=obj1;
如果类FOO有拷贝构造函数,那么在定义时就会调用拷贝构造函数,在对象的生存周期结束时,调用对象的析构函数。上面这段程序的输出结果就是:
- Constructing. // constructing obj1
- Copy constructing. // constructing obj2
- Destructing. // destructing obj2
- Destructing. // destructing obj2
[阳光梦:上面说明,当用一个已初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候,拷贝构造函数就会被自动调用。]
将一个对象作为参数按值调用方式传递给另一个对象时生成对象副本
将对象按值调用作为函数的参数时,在函数的开始将生成一个临时对象,如果对象有拷贝构造函数,就使用拷贝构造函数对临时对象进行初始化,然后在函数结事调用临时对象的析构函数,例如:
- void get_object(pobj) {
- FOO obj;
- return;
- }
在上面这段代码中,先执行的是参数pobj的初始化,然后再进行局部对象变量obj的初始化。以上代码输出结果如下:
- Copy constructing. // copy constructing pobj
- Constructing. // constructing obj
- Destructing. // destructing obj
- Destructing. // destructing pobj
[阳光梦:上面说明,当用一个已初始化过了的自定义类类型对象作为函数参数以 值传递 时候,拷贝构造函数就会被自动调用。]
生成一个临时对象作为函数的返回结果
生成一个临时对象作为函数的返回结果时,如果返回结果有拷贝构造函数就会调用返回结果的拷贝构造函数进行初始化,而且是在return语句执行时进行,并且在return完成时调用析构函数,例如:
- FOO get_object() {
- FOO obj;
- return obj;
- }
- FOO myobj;
- myobj=get_object();
在函数get_object()内部,先进行obj的初始化,在执行到return语句时,创建一个临时对象,调用拷贝构造函数用obj作为参数对临时对象进行初始化,而之后则是先进行函数内部对象变量的析构,再进行临时变量的析构,因为临时变量要在函数外部对可能接收返回值的变量进行赋值。以上代码的输出结果如下:
- Constructing. // constructing myobj
- Constructing. // constructing obj
- Copy constructing. // copy constructing temp obj
- Destructing. // destructing obj
- Destructing. // destructing temp obj
- Destructing. // destructing myobj
就这样,在VC调试了3个例子就可以差不多弄清拷贝构造函数的调用时间了。
[阳光梦:上面说明,当用一个已初始化过了的自定义类类型对象在函数中以临时对象返回,拷贝构造函数就会被自动调用。]
阳光梦总结:拷贝构造何时被调用,即用已存在的对象初始化新的对象时候被调用。
深拷贝和浅拷贝可以简单理解为:
如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,
反之,没有重新分配资源,就是浅拷贝。
如果在类中没有显式地声明一个拷贝构造函数,那么,编译器将会自动生成一个默认的拷贝构造函数,该构造函数完成对象之间的位拷贝
(就是把对象里的数据成员完全复制给另一个对象,注意:函数成员是共享的)。
下面举个深拷贝的例子。
#include <iostream>
#include <string.h>
using namespace std;
class MyString {
public:
MyString(int strLen, char *str);
MyString(const MyString &oldObj);
~MyString();
private:
int PstrLen;
char *Pstr;
};
MyString::MyString(int strLen, char *str)
{
cout<<"gou zao "<<endl;
PstrLen = strLen;
Pstr = NULL;
Pstr = new char[strLen];
memcpy(Pstr,str,strLen);
cout<<Pstr<<endl;
}
MyString::MyString(const MyString &oldObj)
{
cout<<"copy gou zao "<<endl;
Pstr = NULL;
Pstr = new char[oldObj.PstrLen];
memcpy(Pstr,oldObj.Pstr,oldObj.PstrLen);
cout<<Pstr<<endl;
}
MyString::~MyString()
{
if(Pstr != NULL)
delete Pstr;
}
int main()
{
MyString strOld(strlen("nihao"), "nihao");
MyString strNew(strOld);
return 0;
}
何时调用赋值构造函数
赋值构造函数是通过重载赋值操作符实现的,这种操作符的原型如下:
Class_name& Class_name::operator=(const Class_name&);
它接受并返回一个指向类对象的引用。例如,String 类的赋值操作符的原型如下:
String& String::operator=(const String&);
总结: 将已有的对象赋给另一个已有对象时,将使用重载的赋值操作符:
String headline1("test");
String knot;
knot = headline1;