析构函数、赋值操作符与复制构造函数关系的分析
2011-11-10 15:46
260 查看
类设计三法则:如果类需要显式定义析构函数,则它也需要赋值操作符和复制构造函数
关于这个法则需要注意两点:
1.如果类成员变量中有指向动态分配的内存,则一定需要显式定义析构函数,从而需要定义赋值操作符和复制构造函数
2.如果一个类需要提供显式的复制和赋值操作定义,则将这个类的对象作为数据成员的类不需要额外提供复制和赋值操作定义,也就是说一个类缺省的复制和赋值操作递归的依赖于底层类的成员的复制和赋值,那个类也不需要析构函数,下面是一个能证明这个说法的例子:
运行结果:
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版本的结构体那样仅实施按位复制,它只向内建类型成员变量实施按位复制,默认的析构函数也递归的依赖于底层类的析构函数
关于这个法则需要注意两点:
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版本的结构体那样仅实施按位复制,它只向内建类型成员变量实施按位复制,默认的析构函数也递归的依赖于底层类的析构函数
相关文章推荐
- 复制构造函数,赋值操作符、关系操作符,const函数
- 继承中,构造函数,赋值操作符,析构函数与虚函数的关系
- Copy Control 复制控制 (复制构造函数 copy constructor,析构函数 destructor, 赋值操作符 operator=
- 【C\C++学习】之十八、C++11六大函数(构造函数,移动构造函数,移动赋值操作符,复制构造函数,赋值操作符,析构函数)
- 复制构造函数、析构函数、赋值操作符缺少其一的后果
- 一个实例介绍C++复制控制(有一个C++11新特性要注意)——复制构造函数、赋值操作符、析构函数
- C++11六大函数(构造函数,移动构造函数,移动赋值操作符,复制构造函数,赋值操作符,析构函数)
- 重温C++ primer 之复制控制(复制构造函数,赋值操作符,析构函数)
- 13.1——复制构造函数,赋值操作符,析构函数
- C++类中的4个特殊函数 - 缺省构造函数、拷贝构造函数、拷贝赋值操作符和析构函数
- C++在单继承、多继承、虚继承时,构造函数、复制构造函数、赋值操作符、析构函数的执行顺序和执行内容
- C++在单继承、多继承、虚继承时,构造函数、复制构造函数、赋值操作符、析构函数的执行顺序和执行内容
- C++中自定义类禁止使用复制构造函数和赋值操作符
- 编写合适的构造函数、拷贝构造函数、赋值操作符、析构函数(后三者一般同时出现,称为“大三元法则”),那么这个概念就完整并且不用再考虑其资源(内存)管理的问题。
- 深入C++中构造函数、拷贝构造函数、赋值操作符、析构函数的调用过程总结
- C++在单继承、多继承、虚继承时,构造函数、复制构造函数、赋值操作符、析构函数的执行顺序和执行内容
- C++技术感想 -- 禁止类的复制构造函数和赋值操作符的使用
- C++在单继承、多继承、虚继承时,复制构造函数。赋值构造函数、析构函数的执行顺序
- Effective c++(笔记) 中关于构造函数、析构函数以及赋值操作符的知识
- C++ 第13章 复制控制---复制构造函数、赋值操作符、析构函数