1,复制构造函数综述
复制构造函数(copy constructor)是构造函数的一种,用于将新对象
初始化为另一同类型对象的副本的构造函数。
复制构造函数的(唯一)形参是本类类型对象的引用,且常用const限定。
注意复制与赋值的区别:
对象的赋值是对一个已存在的对象赋值,因此必须先定义被赋值的对象,才能进行赋值。而对象的复制则是从无到有地建立一个新对象,并使它与一个已有的对象完全相同(包括对象的结构和成员的值),类似于克隆。
2,调用复制构造函数的情形
1,根据另一个同类型的对象显式或隐式初始化一个对象(使用等号和使用括号,哪个是显式哪个是隐式?反正调用的都是复制构造函数)
2,复制一个对象,将它作为实参传给一个函数(即函数输入参数为类类型,且不是引用)
3,从函数返回时复制一个对象(即函数的返回类型为类类型, 且不是引用)
4,初始化顺序容器中的元素
C<T> c(n) 调用n次默认构造函数
C<T> c(n,t)调用n次复制构造函数
5,根据元素初始化列表,初始化数组元素
举例如下:
class People
{
public:
People() :name("nobody"), age(0) {}//默认构造函数
People(string new_name, int new_age) :name(new_name), age(new_age){}//一般构造函数
People(const People &new_People);//声明自己的复制构造函数
inline void show()const;//普通成员函数
private:
string name;
unsigned age;
};
//复制构造函数的定义
People::People(const People &new_People)
{
// 把元素逐一复制
name = new_People.name;
age = new_People.age;
// 尝试另一种写法
//*this = new_People; //这种写法实际上是用了赋值操作符,不应该用这种写法
cout << "calling the copy constructor." << endl;
}
void People::show() const
{
cout <<"People的成员函数show内:"<< name << ": " << age << endl;
return;
}
int main()
{
/*******************************************************************/
// 复制构造函数
/*******************************************************************/
People HY("HuangYang", 27);
People WHY("WangHaiyan", 26);
// 1,初始化另一个对象
cout << "复制出另外两个对象" << endl;
People Myself(HY); //说好的直接初始化呢?
People You = WHY;
// 2.1,作为实参传递给函数和从函数返回
cout << "\n\n" << "调用show_info()" << endl;
show_info(HY);
// 2.2,使用引用类型时,不调用复制构造函数
cout << "\n\n" << "调用show_info_ref()" << endl;
show_info_ref(Myself);
// 3,初始化顺序容器
cout << "\n\n" << "创建含有5个Peple的容器" << endl;
vector<People> group(5,HY);
cout << "\n\n" << "创建含有3个没有指定值的Peple的容器" << endl;
vector<People> group_2(3);
// 4,用元素初始化列表初始化数组
cout << "\n\n" << "创建含有2个Peple的数组" << endl;
People us[2] = {HY,WHY};
system("pause");
return 0;
}
最终输出为:
3,合成的复制构造函数
如果我们定义了自己的构造函数,编译器就不会再合成默认构造函数;
但是即便我们定义了构造函数,编译器也还是会合成复制构造函数,除非我们定义自己的复制构造函数。
合成复制构造函数的行为是执行逐个成员初始化,编译器依次复制对象的每个
非static成员。如果存在数组成员,则数组里的每个元素都会复制过去。
如果数据成员为引用类型,那么合成的复制构造函数会使得两个对应的引用绑定到同一个对象。指针类型也是如此。示例如下:
#include <iostream>
using std::cout;
using std::endl;
class A {
public:
A() : x1(3), x2(x1){}
int x1;
int &x2;
};
int main()
{
A a;
A b = a;
cout << "\n打印地址"<< endl;
cout << "&a.x1=" << &a.x1 << " &a.x2=" << &a.x2 << endl;
cout << "&b.x1=" << &b.x1 << " &b.x2=" << &b.x2 << endl;
}
执行的结果如下:
打印地址
&a.x1=0x7ffc81aaf0a0 &a.x2=0x7ffc81aaf0a0
&b.x1=0x7ffc81aaf0b0 &b.x2=0x7ffc81aaf0a0
结果表明,a.x1、a.x2、b.x2这3个成员有相同的地址。
4,定义自己的复制构造函数
在例子中我们已经定义了一个自己的复制构造函数,让它除了实现复制的功能外还打印了一句话,以证明确实在调用复制构造函数。
必须定义自己的复制构造函数的情形:
1,数据成员里有指针;
2,构造函数中需要分配内存;
3,执行某些特定工作(比如打印一句话?);
5,禁止复制
将复制构造函数设为私有,则复制构造函数就无法调用。
如果要连友元和成员中的复制也禁止,则必须声明一个private的复制构造函数但并不对其进行定义。
注意:复制构造函数也是构造函数,如果定义了自己的复制构造函数,编译器就不会合成默认构造函数了。所以一般定义了复制构造函数也就需要自己定义默认构造函数。