《Effective C++》条款14 总是让base class拥有virtual destructor
有时,一个类想跟踪它有多少个对象存在。一个简单的方法是创建一个静态类成员来统计对象的个数。这个成员被初始化为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的更多相关文章
- Effective C++ 条款14
在资源管理器中小心copying行为 上节是对资源的管理说明.有时候我们不能依赖于shared_ptr或者auto_ptr,所以我们须要自己建立一个资源管理类来管理自己的资源. 比如建立一个类来管理M ...
- Effective C++ -----条款14: 在资源管理类中小心copying行为
复制RAII对象必须一并复制它所管理的资源,所以资源的copying行为决定RAII对象的copying行为. 普遍而常见的RAII class copying行为是:抑制copying(使用私有继承 ...
- Effective C++ 条款七 为多态基类声明virtual析构函数
class TimeKeeper { public: TimeKeeper(); // ~TimeKeeper(); 错误,此作为一个基类,被继承了.其继承类被delete后,基类被销毁,但继承类可能 ...
- More Effective C++ 基础议题(条款1-4)总结
More Effective C++ 基础议题(条款1-4)总结 条款1:仔细区别pointers和references 如果有一个变量,其目的是用来指向(代表)另一个对象,但是也有可能它不指向(代表 ...
- Effective C++ -----条款18:让接口容易被正确使用,不易被误用
好的接口很容易被正确使用,不容易被误用.你应该在你IDE所有接口中努力达成这些性质. “促进正确使用”的办法包括接口的一致性,以及与内置类型的行为兼容. “阻止误用"的办法包括建立新类型.限 ...
- [More Effective C++]条款22有关返回值优化的验证结果
(这里的验证结果是针对返回值优化的,其实和条款22本身所说的,考虑以操作符复合形式(op=)取代其独身形式(op),关系不大.书生注) 在[More Effective C++]条款22的最后,在返回 ...
- More Effective C++ 条款0,1
More Effective C++ 条款0,1 条款0 关于编译器 不同的编译器支持C++的特性能力不同.有些编译器不支持bool类型,此时可用 enum bool{false, true};枚举类 ...
- Effective C++_笔记_条款07_为多态基类声明virtual析构函数
(整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) 这个规则只适用于polymorphic(带多态性质的)base ...
- [Effective C++系列]-为多态基类声明Virtual析构函数
Declare destructors virtual in polymorphic base classes. [原理] C++指出,当derived class对象经由一个由base clas ...
随机推荐
- Android(java)学习笔记184:生成 4种 不同权限的文件
1.首先我们编写一个生成 4种 不同权限的文件的程序案例: (1)首先是activity_main.xml文件: <RelativeLayout xmlns:android="http ...
- ASP.NET获取汉字首字母
/// <summary> /// 获取汉字首字母(可包含多个汉字) /// </summary> /// <param name="strText" ...
- enable cors in spring mvc with swagger
a. In controller add @CrossOrigin(origins = "http://localhost:8080") b. In mvc-servlet.xml ...
- day-8
/* 倒数8天了 今天高考报名 两个班主任都来了23333 然后填班主任姓名就尴尬了23333 上午考试 挺不错的题目 都不是很水 但需要思考 而我是一脸的懵逼~~ 下午整理各种板子 主要是图论的 最 ...
- Sliverlight linq中的数组筛选数据库中的数据
首先 什么是linq呢 ? LINQ即Language Integrated Query(语言集成查询),LINQ是集成到C#和Visual Basic.NET这些语言中用于提供查询数据能力的一个新特 ...
- asp.net - GridView根据linkButton值不同跳转不同页面
一,当前页面中,前台界面的主要代码: <asp:TemplateField HeaderText="姓名"> <ItemTemplate> <!--根 ...
- RESTful 架构风格概述
http://blog.igevin.info/posts/restful-architecture-in-general/(非常好) http://blog.igevin.info/posts/re ...
- 消除热块(hot block)
上篇日志提到了,那么高的负载,是存在数据块读竞争,下面介绍几个方法来消除块竟争 查找块竟争 SELECT p1 "file#", p2 "block#", p3 ...
- new、delete用法(一)
第一部分:new的使用: #define DEBUG_NEW new(THIS_FILE, __LINE__)解释 THIS_FILE:表示当前类所处的文件名: __LINE__:表示分配内存操作所在 ...
- vertical-align的深入学习
W3C官方对vertical-align属性的定义有4个方面: (1)vertical-align属性用于定义“周围的文字.inline元素以及inline-block元素”相对于该元素基线的垂 ...