1,什么时候需要定义自己的复制构造函数?
只包含类类型或内置类型数据成员、不含指针的类一般可以使用合成操作,复制、赋值或撤销这样的成员不需要特殊控制。
换言之,如果数据中有指针,为了避免浅复制,就需要定义自己的复制构造函数、赋值操作符和析构函数了。
一旦定义了自己的复制构造函数或赋值操作符,则默认定义就会被覆盖掉。
2,一个复制构造函数的例子
/*******************************************************************/
// 派生类的构造函数
/*******************************************************************/
class Base4
{
public:
Base4(int ii, int jj) :i(ii), j(jj) { cout << "调用基类的一般构造函数" << endl; }//基类的一般构造函数
Base4() :i(0), j(5203132) { cout << "调用基类的默认构造函数" << endl; }//基类的默认构造函数
Base4(const Base4& b4);//复制构造函数
void show_base() { cout << i << "\t" << j<<"\t"; }
public:
int i;
private:
int j;
};
class D4 :public Base4
{
public:
D4():k(0) {}//派生类的默认构造函数
D4(int ii, int jj, int kk) :Base4(ii, jj), k(kk) { }//派生类的一般构造函数
D4(int kk) : k(kk) {}//派生类的一般构造函数,隐式调用基类的默认构造函数
D4(const D4& d4);//派生类的复制构造函数
int k;
void show_d() { show_base(); cout << k << endl; }
};
//复制构造函数的定义
Base4::Base4(const Base4& b4)
{
cout << "调用基类的复制构造函数" << endl;
i = b4.i;
j = b4.j;
}
D4::D4(const D4& d4):Base4(d4) //在初始化列表中使用,与前一节的派生类构造函数定义方法相同
{
cout << "调用派生类的复制构造函数" << endl;
k = d4.k;
}
int main()
{
cout << "先生成一个成员d1(520, 3132, 1314):" << endl;
D4 d1(520, 3132, 1314);
d1.show_d();
cout << "\n开始复制:" << endl;
D4 d2(d1);
d2.show_d();
}
执行效果如下:
注意,在定义派生类的复制构造函数时,用到了基类的复制构造函数,并且是在初始化列表中使用的。这一点与上一节,定义派生类的普通构造函数相同。
3,设想1:把基类的复制构造函数从初始化列表中移到函数定义体中会如何?
其他代码不变,将派生类的复制构造函数的定义改为如下所示:
D4::D4(const D4& d4)
{
Base4(d4);
cout << "调用派生类的复制构造函数" << endl;
k = d4.k;
}
编译直接报错:error C2082: 形参“d4”的重定义
显然,编译器把“Base4(d4);”当成了定义变量d4了。
4,设想2:不显式调用基类的复制构造函数会怎样?
其他代码不变,将派生类的复制构造函数的定义改为如下所示:
D4::D4(const D4& d4)
{
cout << "调用派生类的复制构造函数" << endl;
k = d4.k;
}
执行结果如下:
很明显,当我们不显式调用基类的复制构造函数时,编译器隐式调用了基类的默认构造函数。造成的结果是:基类部分是默认值,派生类部分是另一个对象的值。这种奇怪的现象当然通常不是我们想要的。
5,赋值操作符
赋值操作符与复制构造函数实现类似,不再赘述。
提供一个完整的例子:
class Base4
{
public:
Base4(int ii, int jj) :i(ii), j(jj) { cout << "调用基类的一般构造函数" << endl; }//基类的一般构造函数
Base4() :i(0), j(5203132) { cout << "调用基类的默认构造函数" << endl; }//基类的默认构造函数
Base4(const Base4& b4);//复制构造函数
Base4& operator=(const Base4& b4);
void show_base() { cout << i << "\t" << j<<"\t"; }
public:
int i;
private:
int j;
};
class D4 :public Base4
{
public:
D4() :k(0) { cout << "调用派生类的默认构造函数" << endl; }//派生类的默认构造函数
D4(int ii, int jj, int kk) :Base4(ii, jj), k(kk) { }//派生类的一般构造函数
D4(int kk) : k(kk) {}//派生类的一般构造函数,隐式调用基类的默认构造函数
D4(const D4& d4);//派生类的复制构造函数
D4& operator=(const D4&);
int k;
void show_d() { show_base(); cout << k << endl; }
};
//定义基类的复制构造函数
Base4::Base4(const Base4& b4)
{
cout << "调用基类的复制构造函数" << endl;
i = b4.i;
j = b4.j;
}
//定义基类的赋值操作符
Base4& Base4::operator=(const Base4& b4)
{
cout << "调用基类的=" << endl;
if (this != &b4)
{
i = b4.i;
j = b4.j;
}
return *this;
}
//定义基类的复制构造函数
D4::D4(const D4& d4)
{
cout << "调用派生类的复制构造函数" << endl;
k = d4.k;
}
//定义派生类的赋值操作符
D4& D4::operator=(const D4& d4)
{
cout << "调用派生类的=" << endl;
if (this != &d4)
{
Base4::operator=(d4);//注意这种调用方式
k = d4.k;
}
return *this;
}
//主函数
int main()
{
cout << "先生成一个成员d1(520, 3132, 1314):" << endl;
D4 d1(520, 3132, 1314);
d1.show_d();
cout << "\n开始复制:" << endl;
D4 d2(d1);
d2.show_d();
cout << "\n开始赋值:" << endl;
D4 d3;
d3 = d1;
d3.show_d();
return 0;
}
执行结果:
请注意基类的赋值操作符的调用方式。与基类的复制构造函数不同,基类的赋值操作符应当出现在派生类的赋值操作符的定义体中。
另有一点需要注意,如果我们写: D4 d3 = d1;
实际调用的是复制构造函数,而不是赋值操作符。