复制构造函数与=操作符它们分别会在什么时候被调用?人们常常会被表象所困扰而得不出正确的结论。比如有类ca和函数func(见附录),那么程序片段
ca t1;
ca t2 = t1;
中,第二行所调用的并非=操作符,而是复制构造函数,而
t1 = func(t2);
中函数返回ca对象时,所调用的又是=操作符,而
ca t3 = func(t2);
中函数返回ca对象时,所调用的却是复制构造函数。由此可见,光凭=符号就确定调用了=操作符是不可信的。
复制构造函数
复制构造函数是一个特殊的构造函数,它在声明上与一般的构造函数唯一的一点区别是参数必须而且只能是该类的常量引用,形式如下:
ClassName(const ClassName & O){…}
那么,它在什么时候会被调用呢?很简单,它只是在将存在的对象的值赋给某个只是被声明而未被初始化的对象时被调用。如在某些构造函数的初始化列表中和上面提到的情况。也就是说,复制构造函数只是在进行值传递时是必要的。值得注意的是,复制构造函数在拥有其独特的一面的同时,还是一个构造函数。这就意味着它被使用的地方已经实现了构造函数的职能,不需要再进行构造函数的调用。所以当tca包含ca时,若在tca的构造函数的初始化列表中对ca对象进行初始化时,只需调用ca的复制构造函数,而若在tca的构造函数中进行初始化时,则需要调用ca的构造函数及其=操作符。
=操作符
=操作符用在两个都已经初始化过了的对象的赋值中,形式如下:
ClassName & operator = (const ClassName & T){… ;return *this;}
默认的复制构造函数和=是编译器可以自动产生的函数。当赋值给一个尚未用构造函数初始化的对象时,调用的是复制构造函数;当赋值给一个已经初始化的对象时,调用的是=。而默认的这些函数执行的都是bitwise复制,这对于一些包含指针并需要进行内存动态分配的类来说是灾难性的。所以,针对这种类,必须显式地重新定义复制构造函数和=。
附
程序
#include <iostream>
class ca
{
public:
ca()
{
std::cout<<"I'm constructor!"<<std::endl;
}
ca(const ca & X)
{
std::cout<<"I'm copy-constructor!"<<std::endl;
}
ca & operator = (const ca & X)
{
std::cout<<"I'm = assignment"<<std::endl;
return *this;
}
};
ca func(ca t)
{
return t;
}
class tca
{
public:
ca tk;
tca(ca& k):tk(k)
{
tk = k;
std::cout<<"I'm tca's constructor!/n";
}
};
int main(int argc, char **argv)
{
ca t1;
ca t2 = t1;
ca t3;
t3 = t1;
func(t1);
ca t4 = func(t1);
t3 = func(t1);
tca tc1(t1);
}