构造和析构的那些事
来源:互联网 发布:易语言传奇霸业源码 编辑:程序博客网 时间:2024/05/21 19:27
对《C++对象模型》、《More Effective C++》两书中关于构造以及析构函数的讲解总结:
一.关于default constructors.
“default constructors......在需要的时候被编译出来” 此处是指在编译器需要的时候被编译出来,被合成出来的constructor只执行编译器所需的行动。即使有需要为class合成一个default constructor, 也不会将两个data members val 和 pnext 初始化为0.
class Foo{ public: int val; Foo * pnext;};void foo_bar(){ Foobar; if(bar.val|| bar.pnext) //..do something //..}
“对于class X, 如果没有任何 user-declared constructor, 那么会有一个 default constructor 被隐式(implicitly)声明出来......一个被隐式声明出来的constructor将是一个trival constructor(没啥用的)”
1. 如果一个class 没有任何constructor, 但它内含一个member object,而后者有default constructor,那么个这个class的implicit default constructor 就是"nontrival",编译器需要为该class 合成一个default constructor. 不过这个合成操作只有在constructor 真正需要被调用时才会发生。 C++ 语言要求以"member objects 在class 中的声明顺序" 来调用各个constructors, 以inline的方式安插在程序代码中。
2. 带有default constructor 的 base class
类似道理,如果一个没有任何constructor的class 派生自一个“带有default constructor”的 base class, 那么这个 derived class 的default constructor 会被视为nonvtrival
3. 带有一个virtual Function 的class
class声明或继承一个virtual function
class 派生自一个继承串链,其中有一个或多个virtual base classes vtbl 以及 vptr
4. 带有virtual Base class 的 class
必须使virtual base class 在其每一个 derived class object 中的位置,能够于执行期准备妥当。
C++新手一般有两个误解:
1. 任何class如果没有定义default constructor,就会被合成出一个来。
2. 比那机器合成出来的default constructor 会显示设定"class 内每一个 data member 的默认值"
二.copy constructor 的构造操作
有三种情况,会以一个object的内容作为另一个class object的初值
1.显示的赋值初始化;
2.object当做参数交给某个函数 ;
3.当函数返回一个class object default memberwise initialization;
把每一个内建的或派生的data member(例如一个指针或一个数组)的值,从某个object 拷贝一份到另一个object上。不过它并不会拷贝其中member class object,而是以递归的方式施行memberwise initialization。
一个class 可用两种方式复制得到,一种是被初始化(copy constructor),另一种是被指定(copy assignment operator)。
决定一个copy constructor是否为trival 的标准在于class 是否展现出所谓的 bitwise copy semantics。
三. member initialization list
在下列情况下,为了让你的程序能够被顺利编译,你必须使用member initialization list:
1. 初始化一个reference member;
2. 初始化一个 const member;
3. 调用一个base class 的 constructor, 而它拥有一组参数;
4. 调用一个member class的constructor, 而它拥有一组参数时。
list中的项目顺序是由class中的members 声明顺序决定的,不是由initialization list中的排列顺序决定的,
class X{private: int i; int j;public:X(int val):j(val), i(j) {}};
i(j)其实比j(val)更早执行,因为j一开始没有值,所以i(j)执行结果就是i无法预知。
正确的写法 initialization list 的项目被放在explicit user code 之前。
X(int val): j(val) {i = j;}
简单的说,编译器会对initializationlist 一一处理并可能重新排序,members 的声明顺序。它会被安插一些代码到constructor体内,并置于任何explicit user code 之前。
四. 析构语意学
如果class没有定义destructor, 那么只有在class 内含memberobject(抑或class 自己的base class)拥有destructor 的情况下,编译器才会自动合成出一个来。甚至虽然它拥有一个virtualfunction,都不会。
1. destructor 的函数本体首先被执行;
2.如果class 拥有member class object,而后者拥有destructor,那么它们会以其声明顺序的相反顺序被调用;
3.如果object 内含一个vptr,现在被重新设定,指向适当之base class 的virtual table。
继承体系下的对象构造
constructor 可能内含大量的隐蔽码,因为编译器会扩充每一个constructor,扩充程度视class T 的继承体系而定。一般而言,编译器所做的扩充操作如下:
1. member initialization list 中的data members 初始化操作会被放进 constructor 的函数本体,并以members 的声明顺序为顺序;
2,如果一个member并没有出现在memberinitialization list 中,但它有一个default constructor, 那么该default constructor 必须被调用;
3. 在那之前,如果class object 有virtual table pointers, 它们必须被设定为初值,指向适当的 virtual tables;
4.在那之前,所有上一层的base class constructors 必须被调用,以base class 的声明顺序为顺序(与memberinitialization list 中的顺序没有关联);
5.所有的virtual base class constructors 必须被调用,从左到右,从最深到最浅。
虚拟继承
传统的“constructor扩充现象”并没有发生,这是因为virtual base class 的共享性的缘故。
五. 构造函数以及析构函数中内存泄露的问题
void processAdoptions(istream&dataSource){ while(dataSource) { ALA *pa =readALA(dataSource); pa->processAdoption(); deletepa; }}
每当readALA 被调用,便产生一个新的heap object。如果没有调用delete,这个循环很快便会出现资源泄露的问题。
如果pa->processAdoption抛出一个exception,processAdoption 便会发生一次资源泄露。
简单的解决方法:
voidprocessAdoptions(istream& dataSource){ while(dataSource) { ALA *pa =readALA(dataSource); try{ pa->processAdoption(); } } catch(....) { deletepa; throw; } delete pa; //如果没有exception 被抛出,也要避免资源泄露 }}
代码凌乱, 思考如何把delete动作从process Adoptions函数移动到函数内某个局部对象的destructor内。
解决的办法就是,以一个"类似指针的对象"取代指针pa。如此当这个类似指针的对象被自动销毁的时候,我们可以令其destructor调用delete。 行为类似 smart pointers。
(我们必须明智和审慎的使用智能指针,因为至少需要加上 copy constructor, assignment operator 以及指针仿真函数 operator* 和 operator->)....
以auto_ptr 对象取代原始指针,就不需要担心heap objects没有被删除,即使在exception 被抛出的情况下。
void processAdoptions(istream&dataSource){ while(dataSource) { auto_ptr<ALA>pa(readALA(dataSource)); //pa被声明为auto_ptr<ALA>对象,自动析构 pa->processAdoption(); //循环后不再有delete 语句 }}
一个对象存放"必须自动释放的资源",并依赖该对象的destructor 释放
void displayInfo(const Information&info){ WINDOW_HANDLE w(createWindow()); displayinfo in wondow corresponding to w; destroyWindow(w);}class WindowHandle{public: WindowHandle(WINDOW_HANDLE handle):w(handle) {} ~WindowHandle(){destroyWindow(w);} operatorWINDOW_HANDLE() {return w;}private: WINDOW_HANDLE W; WindowHandle(constWindowHandle&); WindowHandle&operator=(const WindowHandle);}void displayInfo(const Information&info){ WondowHandlew(createWindow()); displayinfo in window vorresponding to w; //坚持把资源封装在对象内,通常可以避免资源泄露}在constructor 内阻止资源泄露(resource leak):BookEntry::BookEntry(const string&name, const string& address, conststring& imageFileName, conststring& audioClipFilename):theName(name),theAddress(address),theImage(0), theAudioClip(0){ if(imageFile!= " ") { theImage = new Image(imageFileName); } if(audioClipFileName!= ""){ theAudioClip = new AutdioClip(audioClipFileName); }}BookEntry:: ~BookEntry(){ delete the Image; //C++保证删除null指针是安全的 deletetheAudioClip;}
可是当 newAudioClip(audiocliopFileName) 出现异常,控制权移出BookEntry constructor, 此时BookEntry 的 destructor 不会调用来删除theImage已经指向的对象。 C++只会析构已构造完成的对象。
- 构造和析构的那些事
- 关于类加载和构造方法的那些事
- 构造函数的那些事
- 构造函数的那些事
- 谈谈构造函数的那些事
- C++构造函数:初始化的那些事
- 读书日记2012/2/12——C++对象的构造与析构(编译器做的那些事)
- 找工作笔试面试那些事儿(5)---构造函数、析构函数和赋值函数
- 找工作笔试面试那些事儿(5)---构造函数、析构函数和赋值函数
- 找工作笔试面试那些事儿(5)---构造函数、析构函数和赋值函数
- 找工作笔试面试那些事儿(5)---构造函数、析构函数和赋值函数
- 构造函数和析构函数的构造规则
- 构造函数那些事1
- JavaScript new与构造函数的那些事
- string类的构造函数,析构函数,拷贝构造函数和赋值构造函数
- 构造和析构的顺序
- 对象的构造和析构
- c++构造和析构的调用
- Scala类型 6:复合类型 & with关键字
- rm 命令
- Self-aware value network in the context of i4.0 工业4.0背景下,具有感知能力的价值网络
- Linux 学习笔记(二)Linux vi以及vim命令详解
- iOS下录音功能的实现
- 构造和析构的那些事
- 小马哥------高仿红米note刷机 主板h19t h19w h19st v00系列 拆机主板图与开机识别图
- Android混淆代码打包实践总结
- Ubuntu14.04中文输入法与中文乱码问题的解决
- 14.HCNA-HNTD——路由器的FTP服务配置
- tomcat下域名的配置,ROOT.xml的作用
- stm32f103rc
- JAVA中的反射机制
- SHELL编程学习笔记