正文

类继承中构造函数和析构函数的调用2007-04-28 16:02:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/Csharpsky/25292.html

分享到:

类继承中构造函数和析构函数的调用

现在,有三个类,类的定义如下

class CA
{
public:
 CA(){cout<<"CA constructor"<<endl;}
 
    ~CA(){cout<<"CA desstructor"<<endl;}

};

class CB:public CA
{
public:
 CB(){cout<<"CB constructor"<<endl;}
 
 ~CB(){cout<<"CB desstructor"<<endl;}
};

class CC:public CB
{
public:
 CC(){cout<<"CC constructor"<<endl;}
 
 ~CC(){cout<<"CC desstructor"<<endl;}
};

CA是爷爷,CB是爸爸,CC是儿子。

那么任何一本C++的书都会讲,构造函数的调用顺序是CA CB CC,析构函数的调用顺序是CC,CB,CA,什么???你的书没讲,靠,扔了吧

于是

(1) int main()
{
   CC p ;
}
这个程序运行结果是
CA constructor
CB constructor
CC constructor

CC desstructor
CB desstructor
CA desstructor

靠,太简单了,一个鸡蛋飞过来了,:(

继续……………………

(2) 再做第二个试验之前,先做一点小小修改
~CA(){cout<<"CA desstructor"<<endl;}  ----->>>
virtual  ~CA(){cout<<"CA desstructor"<<endl;}

修改main 代码如下
int main()
{
   CA * p = new CC();

   delete p;

   return 0;
}

yeah
结果一模一样哦
CA constructor
CB constructor
CC constructor

CC desstructor
CB desstructor
CA desstructor

但是如果把virtual  ~CA(){cout<<"CA desstructor"<<endl;}
virtual 去掉

那么(2)中的运行结果为
CA constructor
CB constructor
CC constructor

CA desstructor

只调了CA的哦,出问题了
这样的话,就会出现基类的构造函数调用了,但是派生类的构造函数没调用,
对象的派生部分不会被销毁,这将导致资源泄漏

所以我们在设计一个类的时候,如果类至少拥有一个虚函数,或者说基类被设计用于多态,在这种情况下,
一个派生类的对象可能通过一个基类指针来进行操作,然后进行销毁,如果这样的话,那么这个基类的析构函数要设置成虚拟的,
有些类虽然是基类,但是不是用于多态的,没有虚函数,没有被设计成允许经由基类接口派生类对象进行操作,那么也无需设成虚析构函数,毕竟增加了开销,
好了,解释清楚了,我们也知道怎么做了,继续试验
(3)
保留CA中的虚析构函数

修改main 代码如下
int main()
{
   CB * p = new CC();

   delete p;

   return 0;
}

运行结果
CA constructor
CB constructor
CC constructor

CC desstructor
CB desstructor
CA desstructor
取消CA中的虚析构函数,那么,CA,CB,CC
中没有虚析构函数
那么3中代码运行结果如下

CA constructor
CB constructor
CC constructor

CB desstructor
CA desstructor

只调到CB的析构哦,

继续试验,CACBCC中,只有CB是虚析构函数
3
中代码运行如下

CA constructor
CB constructor
CC constructor

CC desstructor
CB desstructor
CA desstructor
所以,如果是CB指向派生类,只要CB或者其基类中存在虚析构函数,那么也是所有的析构函数都调用的了

继续………………

(4)
修改main
代码如下
int main()
{
   CA * p = new CC();

   delete p;

   return 0;
}

如果A的析构函数是虚的,那么情况如2,不多说了

如果是CA的析构函数不是虚的,而CB或者CC的析构函数是虚拟的,那么在调用delete p;会出现内存错误

Expression:_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)  
是在释放内存的时候出现这样的错误
上网查了一下,_BLOCK_TYPE_IS_VALID是用来检测内存有效性宏中的一个,这个错误说明指针使用出现了问题

后来想了一想,应该是因为继承类中出现了虚函数,所以多了一个指向虚函数表的指针,而基类中一个虚函数都没有,所以也没有这个指针啦
所以在delete的时候就出现了内存错,事实证明,这个猜想应该是站得住脚的,在CA中添加一个虚函数,即使的空的虚函数,也不会出现内存错,
关于这个问题,我想在下次继续讨论吧,这里不深入进去了,
回到正题,在CA中添加一个空的,任意的虚拟函数以后,运行正确了,
运行结果是
CA constructor
CB constructor
CC constructor

CA desstructor

这与(2)中的情况是一样的,只要CA的析构函数不是虚拟的,就只能调用CA的析构了


最后来看一种非常Bt的做法
(5)
CA CB CC
中的析构函数,谁是虚拟的,无所谓,随便

修改main 代码如下
int main()
{
   void * p = new CC();

   delete p;

   return 0;
}

运行结果
CA constructor
CB constructor
CC constructor

下面呢???下面没有了,……………………

这种情况,构造了一个cc的对象,然后呢,没有调析构函数,直接把申请的内存释放了,最好不要这样用咯。
好了,最后,上面,基本上把所有的,我能想到的情况都整理了一下,
总结一下

C1 * p = new C2();
delete p;
这样的代码

这里,C1C2的基类,C1可能是C2的爸爸,可能是爷爷,可能是爸爸的爷爷,可能是爷爷的爷爷…………………………

那么首先,调用的构造函数是
C2的第一个祖先一直到C2………………。和C1是什么没关系

delete p的时候,那么有以下几种情况:
1) C1
或者C1的祖先(基类)中,含有虚析构函数,那么调用的析构函数的顺序是从C2一直到C2的第一个祖先
2
)如果C1或者C1的祖先中,没有一个是类是含有虚析构函数的,那么调用的是从C1一直到C1(也是C2)的第一个祖先的
3
)如果C1void,那就什么析构都不调用了。

如果一个类,作为多态的基类,那么尽量把析构函数声明成虚拟的,不然……………… 

好了,就此打住吧,关于4中的内存错误,下次再谈论吧,项目要开始了,又要忙了,哭去了……………………

 

阅读(6752) | 评论(1)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

loading...
您需要登录后才能评论,请 登录 或者 注册