拷贝构造函数的使用
拷贝构造函数
拷贝构造函数用于将一个对象复制到新创建的对象中。它的函数原型:
Class_A(const Class_A&);
需要注意的是它的参数类型必须是引用,形如Class_A(const Class_A),相当于按值传递,会调用该类的拷贝构造函数,从而造成无穷递归的调用拷贝构造函数。
调用拷贝构造函数的时机
- 用一个已存在的类的对象去初始化该类新建立的对象
(1)声明一个对象并初始化为现有对象时。
(2)当函数的形参是类的对象(值传递)时。
(3)当函数的返回值是类的对象时。(可能会被编译器优化从而没有调用)
建议传递引用,节省时间和空间
#第一种情况
Class A{};
A a; #现有对象
A b(a); #类的一个对象a初始化类的另一个对象b
A c=a; #类的一个对象a初始化类的另一个对象c
#第二种情况
void fun1(A p){ return; }
void main(){
A a;
fun1(a);#a实参初始化形参p
}
#第三种情况
A fun2(){
A a;
return a;
}
int main(){
A a;
A d=fun2(a);#函数fun2返回一个类对象
}
实验代码演示
#include<iostream>
using namespace std;
class A{
public:
int a;
A():a(0){
cout << "我是构造函数" << endl;
}
A(const A &other){
a=other.a;
cout << "我是拷贝构造函数" << endl;
}
};
void fun1(A p){
return;
}
A fun2(){
A a;
return a;
}
int main(int argc,char const *argv[]){
A s;
A s1 = s;
A s2 (s);
fun1(s);
A s3=fun2();
return 0;
}
注意:在第三种情况下,当函数的返回值是类对象时,系统自动调用拷贝构造函数。这里默认情况下一般会被编译器优化,减少不必要的拷贝构造,所以,具体的返回值可能会因编译器及编译选项的不同而不同。g++编译器对这种情况进行优化,省略了两次复制构造函数的调用,直接将匿名对象转化为该对象,不需要进行额外的内存分配,提高了效率;若g++编译时加参数g++ xxx.cpp -fno-elide-constructors
会关闭返回值优化,就能看到拷贝构造函数的调用
我是构造函数 #A s调用默认构造
我是拷贝构造函数 #A s1=s
我是拷贝构造函数 #A s2(s)
我是拷贝构造函数 #fun1(s)
我是构造函数 #A a
自定义拷贝构造
浅拷贝:如果在类中没有显式地声明一个拷贝构造函数,那么,编译器将会自动生成一个默认的拷贝构造函数,该函数逐个复制非静态成员(非static)
到新对象中,复制的是成员的值。没有分配单独的内存资源。
深拷贝:当类的数据成员中有指针类型时,我们就必须自己定义一个特定的拷贝构造函数,该拷贝构造函数不仅可以实现原对象和新对象之间数据成员的拷贝,而且可以为新的对象分配单独的内存资源,这就是深拷贝构造函数。
什么时候使用深拷贝
当数据成员中没有指针时,浅拷贝是可行的。但当数据成员中有指针时,如果采用简单的浅拷贝,则两类中的两个指针将指向同一个地址,当对象快结束时,会调用两次析构函数,而导致指针悬挂(野指针)。所以这时必须采用深拷贝。
另一种省事的解决拷贝构造问题的办法是禁止用户进行拷贝构造,将拷贝构造函数设定为private:
下一篇博客我将介绍类中存在动态内存指针时拷贝构造函数和析构函数的处理,避免类析构时二次释放的不确定性。