析构函数、赋值操作符与复制构造函数关系的分析

类设计三法则:如果类需要显式定义析构函数,则它也需要赋值操作符和复制构造函数


关于这个法则需要注意两点:

1.如果类成员变量中有指向动态分配的内存,则一定需要显式定义析构函数,从而需要定义赋值操作符和复制构造函数

2.如果一个类需要提供显式的复制和赋值操作定义,则将这个类的对象作为数据成员的类不需要额外提供复制和赋值操作定义,也就是说一个类缺省的复制和赋值操作递归的依赖于底层类的成员的复制和赋值,那个类也不需要析构函数,下面是一个能证明这个说法的例子:

#include <iostream>
#include <string>
using namespace std;
//不能对B使用memcpy函数的原因是显而易见的
class B
{
public:
	int *a;
	B(int m = 0)
	{
		cout << "B的构造函数" << endl;
		a = new int;
		*a = m;
	}
	B(const B& b)
	{
		cout << "B的复制构造函数" << endl;
		a = new int;
		*a = *(b.a);
	}
	B& operator=(const B& b)
	{
		cout << "调用B的赋值操作符" << endl; 
		if(&b != this)
		{
			delete a;
			a = new int;
			*a = *(b.a);
		}
		return *this;
	}
	~B()
	{
		cout << "调用B的析构函数" << endl;
		delete a;
	}

};
//A不需要定义复制构造函数、赋值操作符、析构操作符,但也不能对A使用mencpy函数
class A
{
public:
	B b1;
	B b2;
	A(int m1 = 0, int m2 = 0) : b1(m1), b2(m2) {}
};
void main()
{ 
	A a1(1,1);
	A a2(2,2);

	cout << "测试A是否需要赋值操作符" << endl;
	cout << "a1: " << a1.b1.a << " " << *(a1.b1.a) << " " << a1.b2.a << " " << *(a1.b2.a) << endl;
	cout << "a2: " << a2.b1.a << " " << *(a2.b1.a) << " " << a2.b2.a << " " << *(a2.b2.a) <<endl;
	a1 = a2;
	//memcpy(&a1,&a2,sizeof(A));//注意万万不可对A调用memcpy这种按位拷贝函数
	cout << "a1: " << a1.b1.a << " " << *(a1.b1.a) << " " << a1.b2.a << " " << *(a1.b2.a) <<endl;
	cout << "a2: " << a2.b1.a << " " << *(a2.b1.a) << " " << a2.b2.a << " " << *(a2.b2.a) <<endl;
	
	cout << "测试A是否复制构造函数" << endl;
	A a3(1);
	cout << "a3: " << a3.b1.a << " " << *(a3.b1.a) << " " << a3.b2.a << " " << *(a3.b2.a) <<endl;
	A a4(a3);
	cout << "a4: " << a4.b1.a << " " << *(a4.b1.a) << " " << a4.b2.a << " " << *(a4.b2.a) <<endl;
}

运行结果:

B的构造函数
B的构造函数
B的构造函数
B的构造函数
测试A是否需要赋值操作符
a1: 00036208 1 00036238 1
a2: 00036268 2 00036298 2
调用B的赋值操作符
调用B的赋值操作符
a1: 00036208 2 00036238 2
a2: 00036268 2 00036298 2
测试A是否复制构造函数
B的构造函数
B的构造函数
a3: 00036340 1 00036370 0
B的复制构造函数
B的复制构造函数
a4: 000363A0 1 000363D0 0
调用B的析构函数
调用B的析构函数
调用B的析构函数
调用B的析构函数
调用B的析构函数
调用B的析构函数
调用B的析构函数
调用B的析构函数

总结:

1.如果类有指向动态内存的指针,则这个类需要定义赋值操作符、复制构造函数、析构函数
2.如果类没有直接指向动态内存的指针,但类具有的一些类成员变量需要定义
赋值操作符、复制构造函数和析构函数,则这个类不需要定义赋值操作符、复制构造函数与析构函数

注:默认的赋值操作符和复制构造函数递归的依赖于底层类成员的复制和赋值的定义,它不仅仅像C版本的结构体那样仅实施按位复制,它只向内建类型成员变量实施按位复制,默认的析构函数也递归的依赖于底层类的析构函数


你可能感兴趣的:(析构函数、赋值操作符与复制构造函数关系的分析)