zoukankan      html  css  js  c++  java
  • 虚析构函数的作用

     class ITest 2  { 3  public: 4      virtual void Test() = 0; 5 6  }; 7 8  class CTest : public ITest 9  { 10 public: 11     CTest()  { printf("constructor.n"); }; 12     ~CTest() { printf("destructor.n"); }; 13     virtual void Test() { printf("This is a Test.n"); }; 14 }; 15 16 void TestMain() 17 { 18     CTest *p1 = new CTest; 19     ^~~~~ 20     p1->Test(); 21     delete p1; 22 }

    上述例中,程序会按照设计的意图,正常打印出:

    constructor.

    This is a Test.

    destructor.

    但若将第18行改改为: 18     ITest *p1 = new CTest; 则输出的结果为:

    constructor.

    This is a Test.

    也就是说,CTest类的析构函数并未调用 现在我们对ITest类做一些改造: 5   virtual ~ITest() {}; 再次执行程序,则结果显示为:

    constructor.                                                             This is a Test.                                                          destructor.

    也就是说,我将接口类添加虚析构函数是正确的,即使我们不准备用ITest接口 究其原因,是在delete一个接口类时,由于析构函数不是虚的,系统直接将ITest的析构函数(编译器会自动生成)删除了之,而不去查找真正需要调用的析构函数。

    也许,在一个接口类中定义虚的析构函数,会让人产生歧义。而这正是老大嘲笑我的原因所在。 那么,试着换一种定义  class ITest 2  { 3  public: 4      virtual void Test() = 0; 5      virtual ~ITest() = 0 6  }; 7  ITest::~ITest() {};  //不要写入定义中

    现在语法能够明确表明ITest是一个抽角类,不能产生对象.需要注意的是,第7行必须有实现,否则在链接时会通不过的(因为析构函数、构造函数和其他内部函数不一样,在调用时,编译器需要产生一个调用链)。

    再考虑多重继承的情况

    例2:  class ITest 2  { 3  public: 4      virtual void Test() = 0; 5 6  }; 7 8 9  class IGuest 10 { 11 public: 12     virtual void Guest() = 0; 13 14 }; 15 16 17 class CTest : public ITest, public IGuest 18 { 19 public: 20     CTest()  { printf("constructor.n"); }; 21     ~CTest() { printf("destructor.n"); }; 22     virtual void Test()  { printf("This is a Test.n"); }; 23     virtual void Guest() { printf("I am Guest.n"); }; 24 }; 25 26 void TestMain() 27 { 28     CTest *p1 = new CTest; 29     ^~~~~ 30     p1->Test(); 31     p1->Guest(); 32 33     delete p1; 34 } 35 36 void Test2() 37 { 38     CTest *p1 = new CTest; 39 40     ITest *pTest = dynamic_cast<ITest*>(p1); 41     pTest->Test(); 42     IGuest *pGuest = dynamic_cast<IGuest*>(p1); 43     pGuest->Guest(); 44 45     delete pTest; 46 }

    ?/P>


    执行TestMain函数时,系统会输出以下结果:

     

    construstor.                                                             This is a Test.                                                          I am Guest.                                                              destructor.

    但以另外一种方式来执行,在某个封装中实现了第28行,然后导出指针供外部使用,如Test2函数所示,则系统输出的结果为:

    construstor.                                                            This is a Test.                                                         I am Guest.

    CTest的析构函数仍未被调用。 若将第45行改成: 45     delete pGuest 系统在显示以上结果时会强出错误提示框。ITest是CTest的第一个继承,两者的地址是一致的,而IGuest则要偏移4个字节,但为什么会导致程序crash,仍未有明确的解释。

    将上述代码中缺失的补上: 5      virtual ~ITest() = 0; 7      ITest::~ITest(){};

    13     virtual ~IGuest() = 0; 15     IGuest::~IGuest(){};

    则不论是TestMain函数,或Test2函数(或者调用delete pGuest),结果都显示正确。

    当然,若没有采用封装导出接口,直接对CTest类进行操作,自然不会有遗忘调用析构函数的情况。但无论从程序的健壮性,或者是可扩展性来说,对接口类添加纯虚的析构函数,是百利而无一害的。

     

    当然,并不是要把所有类的析构函数都写成虚函数。因为当类里面有虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样就会增加类的存储空间。所以,只有当一个类被用来作为基类的时候,才把析构函数写成虚函数。另外,纯虚析构函数必须有实现,不然会出现连接错误。

  • 相关阅读:
    【Anagrams】 cpp
    【Count and Say】cpp
    【Roman To Integer】cpp
    【Integer To Roman】cpp
    【Valid Number】cpp
    重构之 实体与引用 逻辑实体 逻辑存在的形式 可引用逻辑实体 不可引用逻辑实体 散弹式修改
    Maven项目聚合 jar包锁定 依赖传递 私服
    Oracle学习2 视图 索引 sql编程 游标 存储过程 存储函数 触发器
    mysql案例~tcpdump的使用
    tidb架构~本地化安装
  • 原文地址:https://www.cnblogs.com/yiranlaobaitu/p/3760010.html
Copyright © 2011-2022 走看看