拷贝构造函数
拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它必须的一个参数是本类型的一个引用变量。
拷贝构造函数,也是必要的时候才会生成,不是百分百
创建一个类
class CA
{
public:
int m_nNumber;
}
创建两个实例
CA obj1;
obj1.m_nNumber = 10;
CA obj2;
obj2 = obj1;
int类型是基础类型,编译器认识,可以直接进行简单的成员变量初始化,原理是值赋值,不需要合成拷贝构造函数
std::cout << obj2.m_nNumber << std::endl; 结果表明obj2被拷贝了,但内部没有拷贝构造函数
再次构造一个类
class CN
{
public:
int m_nNumber;
}
此时在CA中创建一个CN成员 CN obj;
此时
obj1.m_nNumber = 10;
obj1.obj.m_nNumber = 20;
CA obj2 = obj1;
此时也没有拷贝构造函数
由于CN obj中成员变量仅基础类型,所以编译器也把CN作为简单类型进行值复制了
此时在CN中加一个构造函数和拷贝构造函数
拷贝构造函数的前提是由默认构造函数
CN类中创建:
CN(){};
CN(CN & obj){std::cout << "CN拷贝构造函数" << std::endl}
运行程序,有拷贝构造函数
但此时如果没有在主函数中调用拷贝obj2 = obj1和std::cout << obj2.m_nNumber << std::endl;
运行程序,没有拷贝构造函数只有构造函数
在没有进行拷贝行为情况下,编译器清除掉了拷贝构造函数
继承
使CA继承CN
运行程序,没有调用拷贝构造函数
此时调用拷贝CA obj2 = obj1;发现有默认构造函数
当父类有拷贝构造函数,子类涉及拷贝时,父类就会调用拷贝构造,子类也会生成拷贝构造,因为在生成子类对象时,也生成了父类的子对象
虚函数
CA中加上虚函数 virtual void test(){}
有虚函数就有虚函数表,两个对象之间无法直接拷贝虚函数表,编译器生成拷贝构造函数,拷贝虚函数表
编译器对类的调用分析
拷贝构造函数应用原理
class CA
{
public:
int m_nNumber;
CA(){std::cout << "构造函数" << std::endl};
CA(CA & obj){std::cout << "拷贝构造函数" << std::endl};
~CA(){std::cout << "析构函数" << std::endl};
}
CA obj; 调用构造函数
obj.m_nNumber = 111;
CA obj1 = obj;
CA obj2(obj);
CA obj3 = (obj);
运行程序打印,发现调用了一次构造三次拷贝构造
三个拷贝构造的过程:以CA obj1 = obj 为例
一 前半部分 是为对象obj1申请空间,同malloc 不需要调用构造函数
但如果是new新建一个对象时,会调用默认构造函数,new的内部实现是在申请内存后,再调用默认构造函数,delete同理
二 后半部分是obj.CA::CA(obj) 实际调用obj的CA作用域下的拷贝构造函数,将需要拷贝的对象当作参数传进去,相当于obj.func()调用普通函数
对象调用函数一
CA类中再次添加一个函数
void test(CA obj)
{
return;
}
编译器会拷贝一个临时对象作为临时参数传入test函数,返回时析构临时对象
主函数运行:
CA* obj = new CA; 调用默认构造函数
test(*obj); 传参(注意指针,不同于引用)进入test函数,编译器拷贝了一个临时对象作为传入的参数
obj进入test函数,拷贝构造一个临时对象传参给该函数。
进入该函数,返回,临时的对象析构 ,返回main函数
delete obj;调用析构函数,析构boj对象。
运行流程:构造函数 拷贝构造函数 析构函数 析构函数
对象调用函数二
修改test函数
CA test() 函数传进编译器时是 CA test(CA &temp)因为该函数必须有一个对象返回,所以编译器创建一个临时对象
{
CA obj; 调用第一次构造函数
return obj; 调用temp.CA::CA(obj) 将obj拷贝到临时对象temp,返回时调用析构函数,析构obj
}
CA obj = test(); temp临时变量以赋值的方式给obj赋值而非拷贝
运行程序发现调用一次构造函数一次拷贝构造函数一次析构函数
对象调用函数三
添加一个新的函数
void CAtest()
{
std::cout << "CAtest()" << std::endl
}
test().CAtest()
如上个函数可知,利用test生成一个对象后并没有析构。
所以此时调用test()相当于生成了一个匿名对象,所以该语句运行结束后,匿名对象释放,调用析构函数
调用构造 拷贝构造 析构 打印CAtest() 析构