当用一个已初始化过了的自定义类类型对象去初始化另外一个新构造的对象的时候,拷贝构造函数就会被自动调用。也就是说,当类的对象须要拷贝时,拷贝构造函数将会被调用。如下状况都会调用拷贝构造函数:
(1)一个对象以值传递的方式传入函数体
(2)一个对象以值传递的方式从函数返回
(3)一个对象须要经过另一个对象进行初始化。编程
若是在类中没有显式地声明一个拷贝构造函数,那么,编译器将会自动生成一个默认的拷贝构造函数,该构造函数完成对象之间的位拷贝。位拷贝又称浅拷贝,后面将进行说明。数据结构
自定义拷贝构造函数是一种良好的编程风格,它能够阻止编译器造成默认的拷贝构造函数,提升源码效率。
浅拷贝和深拷贝函数
在某些情况下,类内成员变量须要动态开辟堆内存,若是实行位拷贝,也就是把对象里的值彻底复制给另外一个对象,如A=B。这时,若是B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。spa
深拷贝和浅拷贝能够简单理解为:若是一个类拥有资源,当这个类的对象发生复制过程的时候,资源从新分配,这个过程就是深拷贝,反之,没有从新分配资源,就是浅拷贝。下面举个深拷贝的例子。
指针
1.浅拷贝:code
浅拷贝就好比像引用类型对象
浅拷贝是指源对象与拷贝对象共用一份实体,仅仅是引用的变量不一样(名称不一样)。对其中任何一个对象的改动都会影响另一个对象。举个例子,一我的一开始叫张三,后来更名叫李四了,但是仍是同一我的,无论是张三缺胳膊少腿仍是李四缺胳膊少腿,都是这我的倒霉。blog
2.深拷贝:内存
而深拷贝就好比值类型。改变了数据的内存指向,内存分配发生改变。资源
Value(值)对象,如预约义类型Int32,Double,以及结构(struct),枚举(Enum)等。
3.隐式共享:
隐式共享又叫作回写复制。当两个对象共享同一份数据时(经过浅拷贝实现数据块的共享),若是数据不改变,不进行数据的复制。而当某个对象须要改变数据时则执行深拷贝。
void MainWindow::on_pushButton_8_clicked() { QString str1="data"; qDebug() << " String addr = " << &str1 <<", "<< str1.constData(); QString str2=str1; //浅拷贝指向同一个数据块 qDebug() << " String addr = " << &str2 <<", "<< str2.constData(); str2[3]='e'; //一次深拷贝,str2对象指向一个新的、不一样于str1所指向的数据结构 qDebug() << " String addr = " << &str2 <<", "<< str2.constData(); str2[0]='f'; //不会引发任何形式的拷贝,由于str2指向的数据结构没有被共享 qDebug() << " String addr = " << &str2 <<", "<< str2.constData(); str1=str2; //str1指向的数据结构将会从内存释放掉,str1对象指向str2所指向的数据结构 qDebug() << " String addr = " << &str1 <<", "<< str1.constData(); qDebug() << " String addr = " << &str2 <<", "<< str2.constData(); }
实测输出结果以下(括号内是个人分析):
String addr = 0x28c798 , 0x14316660 (str2的指针地址,指向前面同一个QSharedDataPointer,其实就是data1)
String addr = 0x28c798 , 0x1433f2a0 (str2的指针地址,指向一个新的QSharedDataPointer,命名为data2)
String addr = 0x28c798 , 0x1433f2a0 (str2的指针地址,指向data2,可是修改其内容)
String addr = 0x28c79c , 0x1433f2a0 (str1的指针地址,指向data2,不修改其内容,且放弃data1,使之引用计数为零而被完全释放)
String addr = 0x28c798 , 0x1433f2a0 (str2的指针地址,指向data2,不修改其内容)
注意,str1的地址和str1.constData()地址不是一回事。