当前位置导航:炫浪网>>网络学院>>编程开发>>C++教程>>C++进阶与实例

C++中确定基类有虚析构函数

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

 

共3页 首页 上一页 1 2 3 下一页 尾页 跳转到
相关内容
赞助商链接