传统大家认为,如果我们在类中没有定义一个自己的拷贝构造函数,编译器会帮助我们合成一个拷贝构造函数,这个合成的拷贝构造函数也是在必要的时候才合成,那么这个必要的时候是什么时候呢?让我们来一起研究。
先来看一个简单的例子:
#include <iostream>
using namespace std;
class A
{
public:
int m_test;
};
int main()
{
A myal;
myal.m_test = 15;
A mya2 = myal;//重点研究这局程序
return 0;
}
//A mya2 = myal; 分析这行程序,按照传统的观点来看,执行这句程序时,应该要调用拷贝构造函数的,但采用dumpbin 命令行查看时,编译器并没有为A类产生一个拷贝构造函数。那为什么mya2的成员变量m_test就为15呢?原因如下:这个其实是编译器内部的一个手法,成员变量初始化手法,比如int这种简单的类型,直接就按值拷贝过去了,编译器不需要合成拷贝构造函数就能够完成这种简单成员变量的初始化工作。那我们进一步的思考一下,为什么编译器会有这样的成员变量初始化手法呢?(自己思考解释原因如下:C++天生就是要兼容C的,而C天生就是要高效率的,所以C++天生就是高效的,像这样的简单类型的成员变量采用成员变量初始化手法应该要比采用拷贝构造函数来初始化效率会更高,这一点不是在C++11中表现的比较明显的吗?C++11中,采用移动构造函数和移动赋值函数,从而减少拷贝,提高程序性能。)
结论(1):如果一个类A没有拷贝构造函数,但是含有一个类类型CTB的成员变量。该类型CTB含有拷贝构造函数,那么当程序中涉及到类A的拷贝构造时,编译器就会为类A合成一个拷贝构造函数。编译器合成的拷贝构造函数往往是干一些特俗的事情(比如合成类A的拷贝构造去调用CTB的拷贝构造函数),如果只是一些基本类型的成员变量值的拷贝这些事,编译器是不用专门合成拷贝构造函数的。
代码演示:
#include <iostream>
using namespace std;
class CTB
{
public:
CTB(const CTB& obj)
{
cout << "CTB(const CTB& obj)" << endl;
}
CTB()
{
}
};
class A
{
public:
int m_test;
CTB m_ctb;
};
int main()
{
A myal;
myal.m_test = 15;
A mya2 = myal; //用dumpbin 查看 text.txt中是否含有 A(const A& )
return 0;
}
结论(2):如果一个类CTBSon没有拷贝构造函数,但是它有一个父类CTB,父类有拷贝构造函数,当程序中涉及到类CTBSon的拷贝构造的时候,编译器会为它合成一个拷贝构造函数。(原因其实很简单,子类含有父类的一切,当子类发生拷贝时候,必然要拷贝从父类哪里继承来的那一部分,那么父类以及有拷贝构造函数了,那么怎么样拷贝从父类哪里继承来的呢?这个时候必须用自己类的拷贝构造函数去调用父类的拷贝构造函数了。所以这个时候编译器才为它合成一个拷贝构造函数。)
代码演示:
#include <iostream>
using namespace std;
class CTB
{
public:
CTB()
{
}
CTB(const CTB& obj)
{
cout << "CTB(const CTB& obj)" << endl;
}
};
class CTBSon :public CTB
{
public:
};
int main()
{
CTBSon myctbson1;
CTBSon myctbson2 = myctbson1;
return 0;
}
结论(3):如果一个类CTBSon没有拷贝构造函数,但是该类声明了或者继承了虚函数,当程序中有涉及到类CTBSon的拷贝构造时候,编译器就会为CTBSon合成一个拷贝构造函数。
结论(4):如果一个类没有拷贝构造函数,但是该类含有虚基类,当程序中有涉及到类的拷贝构造时,编译器会为该类合成一个拷贝构造函数。