有时,一个类想跟踪它有多少个对象存在。一个简单的方法是创建一个静态类成员来统计对象的个数。这个成员被初始化为0,在构造函数里加1,析构函数里减1。(条款m26里说明了如何把这种方法封装起来以便很容易地添加到任何类中,“my article on counting objects”提供了对这个技术的另外一些改进)

设想在一个军事应用程序里,有一个表示敌人目标的类:

class enemytarget {
public:
enemytarget() { ++numtargets; }

enemytarget(const enemytarget&) { ++numtargets; }
~enemytarget() {
--numtargets; }

static size_t numberoftargets()
{ return numtargets; }

virtual bool destroy(); //
摧毁enemytarget对象后
// 返回成功

private:
static size_t numtargets; // 对象计数器
};

// 类的静态成员要在类外定义;
// 缺省初始化为0
size_t enemytarget::numtargets;

这个类不会为你赢得一份政府防御合同,它离国防部的要求相差太远了,但它足以满足我们这儿说明问题的需要。

敌人的坦克是一种特殊的敌人目标,所以会很自然地想到将它抽象为一个以公有继承方式从enemytarget派生出来的类(参见条款35及m33)。因为不但要关心敌人目标的总数,也要关心敌人坦克的总数,所以和基类一样,在派生类里也采用了上面提到的同样的技巧:

class enemytank: public enemytarget {
public:
enemytank() {
++numtanks; }

enemytank(const enemytank& rhs)
: enemytarget(rhs)
{
++numtanks; }

~enemytank() { --numtanks; }

static size_t numberoftanks()
{ return numtanks; }

virtual bool destroy();

private:
static size_t numtanks; // 坦克对象计数器
};

(写完以上两个类的代码后,你就更能够理解条款m26对这个问题的通用解决方案了。)

最后,假设程序的其他某处用new动态创建了一个enemytank对象,然后用delete删除掉:

enemytarget *targetptr = new enemytank;

...

delete targetptr;

到此为止所做的一切好象都很正常:两个类在析构函数里都对构造函数所做的操作进行了清除;应用程序也显然没有错误,用new生成的对象在最后也用delete删除了。然而这里却有很大的问题。程序的行为是不可预测的——无法知道将会发生什么。

c++语言标准关于这个问题的阐述非常清楚:当通过基类的指针去删除派生类的对象,而基类又没有虚析构函数时,结果将是不可确定的。这意味着编译器生成的代码将会做任何它喜欢的事:重新格式化你的硬盘,给你的老板发电子邮件,把你的程序源代码传真给你的对手,无论什么事都可能发生。(实际运行时经常发生的是,派生类的析构函数永远不会被调用。在本例中,这意味着当targetptr
删除时,enemytank的数量值不会改变,那么,敌人坦克的数量就是错的,这对需要高度依赖精确信息的部队来说,会造成什么后果?)

为了避免这个问题,只需要使enemytarget的析构函数为virtual。声明析构函数为虚就会带来你所希望的运行良好的行为:对象内存释放时,enemytank和enemytarget的析构函数都会被调用。

和绝大部分基类一样,现在enemytarget类包含一个虚函数。虚函数的目的是让派生类去定制自己的行为(见条款36),所以几乎所有的基类都包含虚函数。

如果某个类不包含虚函数,那一般是表示它将不作为一个基类来使用。当一个类不准备作为基类使用时,使析构函数为虚一般是个坏主意。请看下面的例子,这个例子基于arm(“the annotated c++ reference manual”)一书的一个专题讨论。

《Effective C++》条款14 总是让base class拥有virtual destructor的更多相关文章

  1. Effective C++ 条款14

    在资源管理器中小心copying行为 上节是对资源的管理说明.有时候我们不能依赖于shared_ptr或者auto_ptr,所以我们须要自己建立一个资源管理类来管理自己的资源. 比如建立一个类来管理M ...

  2. Effective C++ -----条款14: 在资源管理类中小心copying行为

    复制RAII对象必须一并复制它所管理的资源,所以资源的copying行为决定RAII对象的copying行为. 普遍而常见的RAII class copying行为是:抑制copying(使用私有继承 ...

  3. Effective C++ 条款七 为多态基类声明virtual析构函数

    class TimeKeeper { public: TimeKeeper(); // ~TimeKeeper(); 错误,此作为一个基类,被继承了.其继承类被delete后,基类被销毁,但继承类可能 ...

  4. More Effective C++ 基础议题(条款1-4)总结

    More Effective C++ 基础议题(条款1-4)总结 条款1:仔细区别pointers和references 如果有一个变量,其目的是用来指向(代表)另一个对象,但是也有可能它不指向(代表 ...

  5. Effective C++ -----条款18:让接口容易被正确使用,不易被误用

    好的接口很容易被正确使用,不容易被误用.你应该在你IDE所有接口中努力达成这些性质. “促进正确使用”的办法包括接口的一致性,以及与内置类型的行为兼容. “阻止误用"的办法包括建立新类型.限 ...

  6. [More Effective C++]条款22有关返回值优化的验证结果

    (这里的验证结果是针对返回值优化的,其实和条款22本身所说的,考虑以操作符复合形式(op=)取代其独身形式(op),关系不大.书生注) 在[More Effective C++]条款22的最后,在返回 ...

  7. More Effective C++ 条款0,1

    More Effective C++ 条款0,1 条款0 关于编译器 不同的编译器支持C++的特性能力不同.有些编译器不支持bool类型,此时可用 enum bool{false, true};枚举类 ...

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

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

  9. [Effective C++系列]-为多态基类声明Virtual析构函数

    Declare destructors virtual in polymorphic base classes.   [原理] C++指出,当derived class对象经由一个由base clas ...

随机推荐

  1. Android Studio导入aar依赖文件

    以shareSDK为例,导入SMSSDK-2.1.1.aar: 首先将这个aar文件粘贴到libs文件夹下,然后在app目录下的build.gradle里操作 repositories{ flatDi ...

  2. jquery插件下载地址

    以下是本人收集的jquery插件下载地址: .............版本自行选择. jquery官网:http://jquery.com/ jquery.validate.js 官网下载地址:htt ...

  3. gitcafe 使用hexo搭建博客

    --缘由:因为看大家都用github等搭建博客,作为半个程序员的自己,也按捺不住了,终于有空来尝试一把了,选择了和github 相同功能的gitcafe网站,因为在国内比较快,这是大家的看法,下面写一 ...

  4. php strtotime函数服务器和本地不相同

    遇到过一种情况strtotime 在本地和服务器不相同 服务器返回的是-1 strtotime($sa_sagyo_ymd."23:59:59") 如果这样用不了,就只能换一种写法 ...

  5. 初识 .NET平台下作业调度器——Quartz.NET

    Quartz.NET是一个开源的作业调度框架,是OpenSymphony 的 Quartz API的.NET移植,它用C#写成,可用于winform和asp.net应用中.它提供了巨大的灵活性而不牺牲 ...

  6. 前端开发构建工具gulp的安装使用

    曾几何时还在使用grunt作为前端的构建工具,直到有一天同事向我推荐了gulp,在这里博主将不讨论gulp与grunt各自优势的比较,只为大家介绍gulp如何安装和使用. Gulp 是用 nodejs ...

  7. 在调用Qt库来实现功能过程中的一些总结

    1.对于QTabWidget中tab名字的变化.当其中只有一个&时,Qt Assistant中给出的解释是:If the tab's label contains an ampersand, ...

  8. COM简单应用示例

    使用com技术开发模式进行的示例. com技术关键部分源码:主要将所有接口都写入到这个文件中 testinterface.h #ifndef TESTINTERFACE_H #define TESTI ...

  9. Codeforces 551E - GukiZ and GukiZiana(分块)

    Problem E. GukiZ and GukiZiana Solution: 先分成N=sqrt(n)块,然后对这N块进行排序. 利用二分查找确定最前面和最后面的位置. #include < ...

  10. jQuery仿苏宁易购导航

    最近看了些网上的各类导航网站源码,自己学习制作了一个仿苏宁易购的导航栏 jQuery部分代码 $(function(){ $(".CategoryTree>ul>li" ...