1、拷贝构造函数
定义:如果一个类的构造函数的第一个参数是类自身类类型的引用,且任何额外参数都有默认值,则此构造函数为拷贝构造函数。
由此可见,拷贝构造函数是一种特殊的构造函数,一个类可以有多个拷贝构造函数,既可以是public的,也可以是private的(特殊用法,如设计模式中的单例模式需要将拷贝构造函数置为private权限以防止外部的拷贝,确保唯一实例):
class A{
public:
A(const A &a);//const拷贝
A(A &a);//非const拷贝
A(A &a, int n = 1);//带其它参数的拷贝
private:
A(const A &a) = delete;//显式拒绝拷贝,C11用法
2、拷贝构造调用的时机
当用一个已初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候,拷贝构造函数就会被自动调用。即,当类的对象需要拷贝时,拷贝构造函数将会被调用。以下情况都会调用拷贝构造函数:
①、一个对象以值传递的方式传入函数体
②、一个对象以值传递的方式从函数返回
③、一个对象需要通过另外一个对象进行初始化(A a(b)或A a = b)
调用时,如果类中有自定义的拷贝构造函数则调用自定义的,否则将调用编译器合成的默认拷贝构造函数。在一些情况下,调用默认拷贝构造会导致错误发生。
3、一个案例
类Person有两个成员变量,其中一个为指针,使用默认的拷贝构造函数实现对象拷贝:
#include <iostream>
using namespace std;
class Person {
private:
int age;
char *name;
public:
Person();
~Person();
};
Person::Person()
{
name = new char(20);
cout << "Person() " << endl;
}
Person::~Person()
{
cout << "~Person() " << (int)name << endl;
delete name;
name = NULL;
}
int main()
{
{
Person p1;
Person p2(p1);
}
system("pause");
return 0;
}
运行结果(VC和centOS)如下:
4、结果分析
由于对象在进行拷贝赋值(准确的说是在离开作用域后析构时)的时候发现类中没有自定义的拷贝构造,所以使用了编译器合成的默认拷贝构造函数进行了指针的拷贝,结果是:两个指针指向同一地址,随后进行了两次delete同一指针导致崩溃。
5、浅拷贝与深拷贝
位拷贝(浅拷贝)只是对指针的拷贝,拷贝后两个指针指向同一个内存空间;
值拷贝(深拷贝)不但对指针进行拷贝,而且对指针指向的内容进行拷贝(重新分配内存空间),经深拷贝后的指针指向的是两个不同地址的指针(深拷贝采用在堆内存中申请新的空间来存储数据,避免悬垂指针的产生)。
默认拷贝构造函数使用的就是浅拷贝,只是对对象中的数据成员进行简单的赋值,如果对象中存在动态成员(如指针),浅拷贝就会出现问题。因此,当一个类中存在指针成员时,应使用自定义的拷贝构造,修改上述代码如下:
#include <iostream>
using namespace std;
class Person {
private:
int age;
char *name;
public:
Person();
~Person();
Person(const Person&);
};
Person::Person()
{
name = new char(20);
cout << "Person() " << endl;
}
Person::~Person()
{
cout << "~Person() " << (int)name << endl;
if(name != NULL)
{
delete name;
name = NULL;
}
}
Person::Person(const Person &s)
{
name = new char(20);
memcpy(name, s.name, strlen(s.name));
cout<<"copy Person"<<endl;
}
int main()
{
{
Person p1;
Person p2(p1);
}
system("pause");
return 0;
}
同样的,对于含有指针类型的结构体进行复制时也存在浅拷贝与深拷贝的问题(如struct node1 = node2,node2中含有指针变量时这种拷贝为浅拷贝),进行深拷贝时必须为第二个结构体的指针变量重新申请空间后再进行拷贝。
6、参考
①、https://www.cnblogs.com/BlueTzar/articles/1223313.html
②、https://blog.csdn.net/caoshangpa/article/details/79226270
③、https://blog.csdn.net/feitianxuxue/article/details/9275979