虚析构函数、虚函数结合考题变种
来源:互联网 发布:java ocr图片文字识别 编辑:程序博客网 时间:2024/05/23 20:31
虚析构函数、虚函数结合考题变种
1.[Effective C++原则07]:为多态基类声明virtual 析构函数。
[如果不]: 如果不声明为析构函数,可能出现的结果如下:Derived对象的成分没有被销毁,形成资源泄露、在调试上会浪费很长时间。
class CSimpleClass{public:CSimpleClass(){ cout << "CSimpleClass" <<endl; }~CSimpleClass() { cout <<"~CSimpleClass" << endl; }private:}; class CDerived : public CSimpleClass{public:CDerived() { cout << "CDerived" << endl; }~CDerived() { cout << "~CDerived" << endl; }private:}; int main(){CSimpleClass *pSimple = new CDerived;delete pSimple; return 0;}
执行结果如下:
显然,CDerived 对象没有被析构!
1、 造成上述不同的原因何在?
“C++标准”明确指出,当派生类对象经由一个基类指针pBaseObject被删除,而该基类带有一个non-virtual析构函数,其结果未有定义(即不可预知)。实际执行时,如上面第一个图示,会产生bug,派生类的对象没有被销毁。
这就形成诡异的“局部销毁”对象,形成资源泄露。
2、 什么时候需要基类析构函数声明为虚函数?什么时候不需要基类的析构函数为虚函数?
该问题涉及析构函数何时应该为虚函数。注意:对于上面的基类BaseClass,
若析构函数不为虚函数,sizeof(BaseClass) = 1。
若析构函数为虚函数,sizeof(BaseClass) = 4。
至于为什么包含构造函数、非虚析构函数的类的大小为1个字节。解释如下:
空类类的大小比如BaseClass没有构造、析构函数,本来sizeof(BaseClass)应该为0,但是我们声明该类型实例的时候,必须在内存中占用一定的空间,否则无法使用这些实例。至于占用多少内存,由编译器决定,visual studio中每个空类型的实例占用1个字节的空间。
而加上构造函数、析构函数或其他非虚类型的函数以后呢?由于这些非虚类型的函数的地址只与类有关,而与类的实例无关,编译器不会因为非虚函数的增加而添加任何额外的信息。
那么为什么析构函数变成虚函数后,大小就变成4个字节了呢?主要原因是:C++一旦发现类中有虚函数,就会为该类生成虚函数表,并在该类型的每一个实例中添加指向虚函数表的指针。在32位机器上,一个指针占4个字节的空间,所以求sizeof大小为4。而在64位机器上,一个指针占用8个字节的空间,因此sizeof大小为8。
即为类析构函数声明为虚析构函数是以付出内存为代价的。所以,无端将所有类的析构函数声明为虚函数,就向从未声明它们是虚函数一样,都是错误的。
总结如下:
(1)带多态性质的基类应用声明一个虚析构函数。如果类中带有任何虚函数,它就应该拥有一个虚析构函数;
(2)设计类的目的如果不作为基类,或者不是为了具备多态性,就不应该声明虚析构函数。
——参考《Effective C++》条款7;《剑指Offer》
2.[Effective 原则09]:绝不在构造和析构过程中调用virtual函数。
【原因】:base class的执行更早于derived class的构造函数,当base class的构造函数执行的时候derived class的成员变量尚未初始化。
【如果不】:执行的结果不会动态联编,依然执行其所在层的虚函数。
【示例如下】:
class CSimpleClass{public: CSimpleClass() { cout << "CSimpleClass"<< endl; foo();} //调用了本层的foo virtual ~CSimpleClass() { cout <<"~CSimpleClass" << endl; foo();} //调用了本层的foo virtual void foo() { cout << "CSimpleClass::foo()" << endl; }private:}; classCDerived : public CSimpleClass{public: CDerived() { cout <<"CDerived" << endl; foo(); } ~CDerived() { cout <<"~CDerived" << endl; foo(); } void foo() { cout<< "CDerived::foo()" << endl; }private:}; int main(){ CSimpleClass *pSimple = new CDerived; delete pSimple; return 0;}
执行结果如下:
3.综合1,2的笔试题如下:
class CBase{public: CBase(){ cout << "CBase ctor" << endl; foo(); } //调用本层的foo ~CBase() { cout<< "CBase dtor" << endl; foo(); } //未加virtual,且调用本层的fooprivate: virtualvoid foo(){ cout << "Base::foo()" << endl; } //}; class CDerived : public CBase{public: CDerived(){cout << "CDerived ctor" << endl; foo(); } ~CDerived(){cout << "CDerived dtor" << endl; foo(); } private: virtual void foo(){ cout << "Derived::foo()" << endl; }}; int main(){ CBase*pBase = new CDerived; delete pBase; return 0;}
结合原则1,2.正确的输出结果是:
显然,1.CDerived的析构函数不会被调用,因为CBase的析构函数非虚函数。
2.在CBase的构造和析构函数中调用虚函数,仅会执行本层的定义,不会下调。还是遵照[Effective 原则09]:绝不在构造和析构过程中调用virtual函数为上策。
- 虚析构函数、虚函数结合考题变种
- C++虚析构函数、虚函数结合考题变种
- C++虚析构函数、虚函数结合考题变种
- C++笔试常考题 虚函数
- C语言函数的两种写法变种
- uva 11440 Help Mr. Tomisu 欧拉函数变种
- 函数重用和函数引用结合测试
- MFC消息机制与虚函数的结合
- 虚函数与泛型的结合应用
- C++中虚函数,结合例子解析,然后总结
- 软件工程——结合软考题
- 虚函数,纯虚函数,抽象类,点H与点CPP文件的结合
- 纯虚函数、虚函数、虚析构函数
- 虚函数,虚析构函数,虚函数表
- 构造函数 虚函数 虚析构函数
- C Primer Plus学习 三十 string.h strcmp()函数和strncmp ()变种
- vi中结合ctag查找函数声明
- 函数对象 与 count_if()结合使用 例子
- Java SE 学习笔记 第六记---代理模式
- 大学生活,一个新的开始
- The c programming language 1-9
- 杭州烟花窜入看台致多人受伤-杭州-烟花-看台
- ubuntu下设置关闭/最小化/最大化按钮左右位置方法
- 虚析构函数、虚函数结合考题变种
- 铁道部公安局:查身份证数量过大做法应纠正-铁道部公安局-查验身份证-纠正
- Centos6,Ubuntu,等linux 的ADSL 上网设置
- 分享别人的两种闹钟的实现方法
- Schema入门介绍
- Schema入门之XMLSchema简介及使用原因
- The programming language 1-13 竖直方向直方图
- 外交部:对莫言获得诺贝尔文学奖表示祝贺-外交部-莫言-祝贺
- Schema入门之XMLSchema使用及元素