首页 | 互联网 | IT动态 | Cisco | Windows | Linux | Java | .Net | Oracle | 华为 | 存储世界 | 服务器 | 网络设备 | IDC | 安全 | 求职招聘
IT培训 | 数字网校 | 技术专题 | 电子书下载 | 教学视频 | 网页设计 | 平面设计 | 解决方案 | 直播室 | 虚拟考场 | 搜索 | 博客 | 沙龙 | 论坛
首页 | JAVA | C# | VB | VB.NET | C/C++ | delphi | 工程管理 | 其他语言 | 论坛
各大城市软件开发培训、软件人才免费咨询热线:400-700-5807
 您现在的位置: 中国IT实验室 >> 桌面开发 >> VC >> 文章正文
C++中确定基类有虚析构函数
来源:ChinaItLab 作者:佚名 时间:2007-8-16

  有时,一个类想跟踪它有多少个对象存在。一个简单的方法是创建一个静态类成员来统计对象的个数。这个成员被初始化为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”)一书的一个专题讨论。

// 一个表示2d点的类
class point {
public:
  point(short int xcoord, short int ycoord);
  ~point();

private:
  short int x, y;
};

  如果一个short int占16位,一个point对象将刚好适合放进一个32位的寄存器中。另外,一个point对象可以作为一个32位的数据传给用c或fortran等其他语言写的函数中。但如果point的析构函数为虚,情况就会改变。

  实现虚函数需要对象附带一些额外信息,以使对象在运行时可以确定该调用哪个虚函数。对大多数编译器来说,这个额外信息的具体形式是一个称为vptr(虚函数表指针)的指针。vptr指向的是一个称为vtbl(虚函数表)的函数指针数组。每个有虚函数的类都附带有一个vtbl。当对一个对象的某个虚函数进行请求调用时,实际被调用的函数是根据指向vtbl的vptr在vtbl里找到相应的函数指针来确定的。

[1] [2] 下一页  

【责编:Youping】
中国IT教育热线咨询
相关文章
编程软件系列之C++编程技巧
如何在C++中动态分配二维数组
使用C++异常来取代exit()函数
Linux下C++异常处理技巧-实例讲解
详细解析C++编写的ATM自动取款机模拟程序
确定项目的工作需求
Delphi与C++之间通过实现函数与对象共享
在Visual Basic 中使用C++ 类
C++ Builder构建算二十四点小游戏
用C++ Builder实现桌布自动更新
推荐文章

 精彩友情推荐
·锐捷交换机报价
·锐捷交换机
·锐捷网络网络交换机
·smc交换机
·smc交换机报价
·IDC资讯大全
·机房品质万里行
·IDC托管必备知识
·全国IDC报价
·网站推广优化
最新更新 推荐文章
·框架:J2EE WEB应用架构分析03-13
·几种VC++数据库开发技术的相对比…03-13
·利用C#实现标注式消息提示窗口03-13
·用C#创建COM对象03-13
·Visual C#多线程参数传递浅析03-13
·Visual C#多线程参数传递浅析03-13
·基于HOOK和MMF的Win密码渗透技术11-15
·Visual C++设计超强仿QQ自动伸缩…11-15
·Java SE 6.0实现高质量桌面集成开…11-15
·史玉柱东山再起幕后高人11-15
·Visual Basic 9.0隐式类型的局部…09-30
·用C#创建COM对象09-06
·IT管理十大失误及其对策08-30
·VC中利用MFC设计绘图程序初步08-23
·JAVA中对象创建和初始化过程08-23
·C语言中的位域的使用08-09
·浅谈Java桌面应用程序开发08-09
·C#的前途如何?08-02
·几种VC++数据库开发技术的相对比较07-12
·用Visual C#实现网络封包监视07-12
·VB.NET中的TextBox控件详解07-12
·VB.NET实现PC与掌上电脑PPC的双向通信07-05