拷贝构造函数
写在前面
- 默认情况下,C++编译器至少会为我们写的类增加三个函数
- 默认构造函数(无参,函数体为空)
- 默认析构函数(无参,函数体为空)
- 默认拷贝构造函数,对类中非静态成员属性进行简单的值拷贝
- 如果用户定义了拷贝构造函数,C++不会再提供任何默认的构造函数
- 如果用户定义了普通构造函数(非拷贝),C++不再提提供默认无参构造,但是会提供默认拷贝构造。
在之前的博文中,我介绍了构造函数和析构函数,这一篇里面,我会对拷贝构造函数单独进行介绍,并介绍深拷贝、浅拷贝等。
1.拷贝构造函数的调用时机
拷贝构造函数的调用时机如下:
- 用一个对象初始化另一个对象
- 对象以值传递的方式给函数参数
- 函数局部对象以值传递的方式从函数返回
下面,我举例说明:
#include <iostream>
using namespace std;
class MyClass
{
public:
MyClass()
{
cout << "默认构造函数" << endl;
}
MyClass(int a)
{
cout << "有参构造函数" << endl;
}
MyClass(const MyClass &m)
{
cout << "拷贝构造函数调用" << endl;
}
~MyClass()
{
cout << "析构函数调用" << endl;
}
int ak;
};
void test()
{
MyClass m1;
m1.ak = 10;
MyClass m2(m1);//用已经创建好的对象来初始化新的对象
}
int main()
{
test();
}
输出结果如下图:
在上面的例子中,我使用了一个已经创建好的对象来初始化一个新的对象。 这也就是拷贝构造函数对的第一个调用时机。
下面是第二个调用时机,即对象以值传递的方式给函数参数。
#include <iostream>
using namespace std;
class MyClass
{
public:
MyClass()
{
cout << "默认构造函数" << endl;
}
MyClass(int a)
{
cout << "有参构造函数" << endl;
}
MyClass(const MyClass &m)
{
cout << "拷贝构造函数调用" << endl;
}
~MyClass()
{
cout << "析构函数调用" << endl;
}
int ak;
};
void dowork(MyClass m)//以值传递方式给函数传参
{
}
void test()
{
MyClass m1;
m1.ak = 10;
dowork(m1);
}
int main()
{
test();
}
输出结果如下图:
最后,是函数局部对象以值传递的方式从函数返回。
#include <iostream>
using namespace std;
class MyClass
{
public:
MyClass()
{
cout << "默认构造函数" << endl;
}
MyClass(int a)
{
cout << "有参构造函数" << endl;
}
MyClass(const MyClass &m)
{
cout << "拷贝构造函数调用" << endl;
}
~MyClass()
{
cout << "析构函数调用" << endl;
}
int ak;
};
MyClass dowork()//函数局部对象以值传递的方式从函数返回
{
MyClass m1;
return m1;
}
void test()
{
MyClass m = dowork();
}
int main()
{
test();
}
输出结果如下:
2.浅拷贝和深拷贝
浅拷贝
同一个类型的对象之间可以赋值,使得两个对象的成员变量的值相同,两个对象仍然是独立的两个对象,这种情况被称为浅拷贝。
一般情况下,浅拷贝没有任何副作用,但是当类中有指针,并且指针指向动态内存分配的内存空间,析构函数做了动态内存释放的处理,会导致内存问题。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Person
{
public:
Person()
{
}
Person(const char *name, int age)
{
Name = (char*)malloc(strlen(name) + 1);
strcpy (Name, name);
Age = age;
}
~Person()
{
cout << "析构函数调用" << endl;
if (Name != NULL)
{
free(Name);
Name = NULL;
}
}
//系统提供默认拷贝构造函数
char *Name;
int Age;
};
void test()
{
Person p1("Aoki", 20);
Person p2(p1);
}
int main()
{
test();
}
输出结果如下:
我们可以看到,错误提示信息中提到了heap.cpp。此外,输出结果中出现了两次析构函数的调用。下面,我尝试尽可能地解释清楚原因。
我们创建了一个对象p1,它有两个属性,一个存放在堆区,一个在栈区,当编译器进行简单的值拷贝时,只是将堆区内容的地址给了新对象p2,接着调用析构函数,释放p1的空间,而存放在堆区的数据也就消失了,紧接着,对象p2也调用析构函数来进行释放。那么,堆区的同一个地址就被释放了两次,引发了异常。图示如下:
这时候,我们就要用到深拷贝了。
深拷贝
当类中有指针,并且此指针有动态分配空间,析构函数做了释放处理,往往需要自定义拷贝构造函数,自行给指针动态分配空间,深拷贝。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Person
{
public:
Person()
{
}
Person(const char *name, int age)
{
Name = (char*)malloc(strlen(name) + 1);
strcpy (Name, name);
Age = age;
}
Person(const Person &p)//深拷贝
{
Age = p.Age;
Name = (char*)malloc(strlen(p.Name) + 1);
strcpy(Name, p.Name);
}
~Person()
{
cout << "析构函数调用" << endl;
if (Name != NULL)
{
free(Name);
Name = NULL;
}
}
char *Name;
int Age;
};
void test()
{
Person p1("Aoki", 20);
Person p2(p1);
}
int main()
{
test();
}
输出结果如下: