引言:

我们都知道类的一个很明显的特性是多态,比如我们声明一个水果的基类:

class Fruit {
public:
Fruit() {};
~Fruit(){};
}

那么我们根据这个Fruit基类可以派生出以下的子类:

   class Apple:public Fruit{};
class Orange:public Fruit{};

那么问题来了,如果我们想经由一个基类指针去删除一个派生类Apple,且我们有以下的方法

Fruit * foo(){
Apple * p = new Apple();
return p; // 子转父,大丈夫
}

接下来似乎顺理成章,我们只需要完成以下的main函数即可了。

int main() {
A *p = foo();
delete p;
return ;
}

但是实际运行就会发现:Apple对象并未被删除!

这是为什么呢?因为foo()返回的指针指向一个Apple对象,其中的Apple的成员变量很可能未被销毁,而且Apple的析构函数也未被执行起来。于是造成了一个局部销毁的局面。

一、解决方案

其实很简单,只需要将基类中的析构函数定义为虚函数即可。

class Fruit {
public:
Fruit() {};
virtual ~Fruit(){}; ←
}

这样在删除p的时候,会先调用Apple的析构函数,然后再调用Fruit的析构函数,最后删除整个Apple对象。

二、扩展

在书中所说:

在string等STL容器使用的时候,即时class不带virtual函数,可能还是会被是否为虚函数的问题折腾。比如我们拿string来作为一个基类声明以下的class

#include <iostream>
#include<string>
using namespace std; class D : public string
{
public:
D(string i) {};
~D() {};
string i;
}; int main()
{
D* d = new D("DD");
string *p;
p = d;
delete p; // 书中描写此处会发生内存泄露,但是十分不解,上面的p =d 是子赋给父,按理是不应该出现问题的 return ;
}

调试结果在VS环境下也不会出现错误,不清楚是为什么,还有待调查。

2014.11.11 调查结果:

在上述结果中,确实不会出现问题,因为只是单纯的删除了p,但是d中间的值有没有得到删除就不能得到确定了。

这跟我们删除d的目的是相悖的。

添加以下的代码来做说明:

class D : public string
{
public:
D(string str, int i): s(str), length(i){};
~D() {
cout << "call me";
};
private:
string s;
int length;
}; int main()
{
D* d = new D("DD", );
string *p;
p = d;
delete p; // 此时释放p,并不会调用~D(),因此原本给length赋值了5并不会被清除!!! return ;
}

三、纯虚析构函数的调用顺序

在基类存在纯虚析构函数的时候,析构函数的运作方式是:派生class的析构函数先被调用,然后再调用基类的析构函数。

 #include <iostream>
#include<string> using namespace std; class A {
public:
A() {};
virtual ~A() = ;
};
A::~A() {
cout << "~A" << endl;
}
class B : public A {
public:
B() {};
~B() {
cout << "~B" << endl;
}
}; int main()
{
B* b = new B();
delete b; return ;
}

运行后输出:

~B
~A

■总结:

1.带多态性质的基类应该声明一个virtual析构函数,如果class带有任何的虚函数,那么它就应该拥有一个virtual析构函数。

2.class的设计目的如果不是作为基类使用,或不是为了具备多态性,就不应该声明virtual析构函数。

[Effective C++ --007]为多态基类声明virtual析构函数的更多相关文章

  1. Effective C++(7) 为多态基类声明virtual析构函数 or Not

    问题聚焦: 已经对一个对象执行了delete语句,还会发生内存泄漏吗? 先来看个demo: // 计时器类 class TimeKeeper { public: TimeKeeper(); ~Time ...

  2. 为多态基类声明virtual析构函数

    一个函数的返回值为基类指针,而当指针指向一个派生类对象,接下来派生类对象被这个基类指针删除的时候,就出现了局部销毁的问题.因为C++指出,当派生类经由一个基类指针被删除,而该基类指针带着一个non-v ...

  3. effective c++(07)之为多态基类声明virtual析构函数

    class TimeKeeper { public: TimeKeeper() ; ~TimeKepper() ; ... } ; class AtomicClock:public TimeKeepe ...

  4. Effective C++_笔记_条款07_为多态基类声明virtual析构函数

    (整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) 这个规则只适用于polymorphic(带多态性质的)base ...

  5. Effective C++ -----条款07:为多态基类声明virtual析构函数

    polymorphic(带多态性质的)base classes应该声明一个virtual析构函数.如果class带有任何virtual函数,它就应该拥有一个virtual析构函数. Classes的设 ...

  6. 【C++】为多态基类声明virtual析构函数

    来自<Effective C++>条款07:为多态声明virtual析构函数 当derived class对象经由一个base class指针被删除,而该base class带着一个non ...

  7. 条款7:为多态基类声明virtual析构函数

    C++明确指出:当派生类对象是由一个基类指针释放的,而基类中的析构函数不是虚函数,那么结果是未定义的.其实我们执行时其结果就是:只调用最上层基类的析构函数,派生类及其中间基类的析构函数得不到调用. # ...

  8. NO.6: 为多态基类声明virtual析构函数

    注意:polymorphic base class 应该具有虚析构函数,如果class带有任何virtual函数,也应具有虚析构函数 class不具备polymorphic属性则不应该声明virtua ...

  9. Effective C++学习笔记 条款07:为多态基类声明virtual析构函数

    一.C++明确指出:当derived class对象经由一个base class指针被删除,而该base class带着一个non-virtual析构函数,其结果未定义——实际执行时通常发生的是对象的 ...

随机推荐

  1. windows 远程桌面连接 RPi 2B

    /************************************************************************* * windows 远程桌面连接 RPi 2B * ...

  2. NoSql数据库使用半年后在设计上面的一些心得 (转)

    http://www.cnblogs.com/AllenDang/p/3507821.html NoSql数据库这个概念听闻许久了,也陆续看到很多公司和产品都在使用,优缺点似乎都被分析的清清楚楚.但我 ...

  3. Ruby准备工作

    解释性语言,自上而下执行,纯面向对象,跨平台,动态绑定,没有多重继承.NetBeans sun公司开发irb指令可快速实时输入并返回结果 quit 或者exitrdoc hello.rb 生成html ...

  4. HTML5 jQuery图片上传前预览

    hTML5实现表单内的上传文件框,上传前预览图片,针刷新预览images,本例子主要是使用HTML5 的File API,建立一個可存取到该file的url,一个空的img标签,ID为img0,把选择 ...

  5. 使用DialogFragment创建对话框总结

    回调Activity中的函数 http://developer.android.com/guide/topics/ui/dialogs.html#PassingEvents 在DialogFragme ...

  6. DbHelper and OracleHelper

    1 连接Sql Server的ConnectionString: Data Source=.;Initial Catalog=ViewVideo;User ID=sa;Password=XXXXXX; ...

  7. Sql2005 全文索引详解

    1.前言 14.1  全文索引的介绍 14.2  全文索引中常用的术语 14.3  全文索引的体系结构 14.4  全文目录管理 14.4.1  创建全文目录 14.4.2  查看与修改全文目录 14 ...

  8. mongo客户端mongo VUE增删改查

    一.先创建一张mongo表,右击已创建的数据库test,点击addcollection.. 输入Collection Name,点击ok: 二.在创建的表中新增列与数据,右击表选择Insert doc ...

  9. python打包成exe(py2exe)

    对比了几个打包工具,发现py2exe更好用一点,一个命令就可以. 1.获取 http://prdownloads.sourceforge.net/py2exe 下载python版本对应的.直接下载然后 ...

  10. 【转载】C/C++语言void及void指针深层探索

    C/C++语言void及void指针深层探索 1.概述许多初学者对C/C++语言中的void及void指针类型不甚理解,因此在使用上出现了一些错误.本文将对void关键字的深刻含义进行解说,并详述vo ...