关于基类析构函数的问题 http://bbs.csdn.net/topics/390543618

来源:互联网 发布:最好的大六壬排盘软件 编辑:程序博客网 时间:2024/06/05 19:57

关于基类析构函数的问题 [问题点数:40分,结帖人liyuanhong13]

 收藏
楼主发表于: 2013-08-10 11:45:46
析构函数基类指针
有这么几个问题:
1.基类析构函数不为虚的话,用派生类指针转化为基类指针之后,删除这个基类指针时,调用的都只是基类的析构函数,是这样吗?
2.如果真的是这样,那我bear *a = new panda;delete a的时候,输出的是:
animal init...
bear init...
panda init...
bear delete...
animal delete...
为甚么animal的析构也调用了?然后是不是可以说,1的说法不是和严谨?有没有一种更严谨的说法?
3.一般情况下,是不是应该把父类的析构函数都设为虚的?比如我这个例子,除了把animal的析构设为虚之外,也应该把bear的析构设为虚?
4.我废话是不是太多了?还是说我的思维太严谨了
C/C++ code
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <iostream>
 
class animal
{
public:
    animal()
    {
        std::cout<<"animal init..."<<std::endl;
    }
    //virtual
    ~animal()
    {
        std::cout<<"animal delete..."<<std::endl;
    }
};
 
class bear : public animal
{
public:
    bear()
    {
        std::cout<<"bear init..."<<std::endl;
    }
 
    //virtual
    ~bear()
    {
        std::cout<<"bear delete..."<<std::endl;
    }
};
 
class panda : public bear
{
public:
    panda()
    {
        std::cout<<"panda init..."<<std::endl;
    }
    ~panda()
    {
        std::cout<<"panda delete..."<<std::endl;
    }
     
};
 
 
int main()
{
    bear *a = new panda;
    //animal *a = new panda;
    delete a;
}
  • <iframe id="iframeu1636200_0" src="http://pos.baidu.com/acom?dc=2&amp;di=u1636200&amp;dri=0&amp;dis=0&amp;dai=2&amp;ps=2839x742&amp;dcb=BAIDU_EXP_SSP_define&amp;dtm=BAIDU_DUP_SETJSONADSLOT&amp;dvi=0.0&amp;dci=-1&amp;dpt=none&amp;tsr=563&amp;tpr=1450182255486&amp;ti=%E5%85%B3%E4%BA%8E%E5%9F%BA%E7%B1%BB%E6%9E%90%E6%9E%84%E5%87%BD%E6%95%B0%E7%9A%84%E9%97%AE%E9%A2%98-CSDN%E8%AE%BA%E5%9D%9B-CSDN.NET-%E4%B8%AD%E5%9B%BD%E6%9C%80%E5%A4%A7%E7%9A%84IT%E6%8A%80%E6%9C%AF%E7%A4%BE%E5%8C%BA&amp;ari=1&amp;dbv=2&amp;drs=1&amp;pcs=1344x815&amp;pss=1344x2869&amp;cfv=0&amp;cpl=6&amp;chi=1&amp;cce=true&amp;cec=UTF-8&amp;tlm=1450182256&amp;ltu=http%3A%2F%2Fbbs.csdn.net%2Ftopics%2F390543618&amp;ltr=https%3A%2F%2Fwww.baidu.com%2Flink%3Furl%3DPDzLxRpXl7BQhv56h4CSGF5BEdxiiUkaKT4amQsQCJY8N7diO4ncsBKxHlu5b5Oc%26wd%3D%26eqid%3Dbd2ab66a00034cb50000000556700315&amp;ecd=1&amp;psr=1440x900&amp;par=1363x900&amp;pis=-1x-1&amp;ccd=24&amp;cja=true&amp;cmi=8&amp;col=zh-CN&amp;cdo=-1&amp;tcn=1450182256&amp;exps=110207&amp;qn=bc971703da70ca59&amp;rdid=1636200&amp;tt=1450182255437.569.997.998&amp;feid=110207" width="200" height="22" align="center,center" vspace="0" hspace="0" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" allowtransparency="true" style="box-sizing: content-box; border-width: 0px; border-style: initial; vertical-align: bottom; margin: 0px;"></iframe>
     
  • <iframe id="iframeu1636201_0" src="http://pos.baidu.com/acom?dc=2&amp;di=u1636201&amp;dri=0&amp;dis=0&amp;dai=3&amp;ps=2946x764&amp;dcb=BAIDU_EXP_SSP_define&amp;dtm=BAIDU_DUP_SETJSONADSLOT&amp;dvi=0.0&amp;dci=-1&amp;dpt=none&amp;tsr=837&amp;tpr=1450182255486&amp;ti=%E5%85%B3%E4%BA%8E%E5%9F%BA%E7%B1%BB%E6%9E%90%E6%9E%84%E5%87%BD%E6%95%B0%E7%9A%84%E9%97%AE%E9%A2%98-CSDN%E8%AE%BA%E5%9D%9B-CSDN.NET-%E4%B8%AD%E5%9B%BD%E6%9C%80%E5%A4%A7%E7%9A%84IT%E6%8A%80%E6%9C%AF%E7%A4%BE%E5%8C%BA&amp;ari=1&amp;dbv=2&amp;drs=1&amp;pcs=1344x815&amp;pss=1344x2983&amp;cfv=0&amp;cpl=6&amp;chi=1&amp;cce=true&amp;cec=UTF-8&amp;tlm=1450182256&amp;ltu=http%3A%2F%2Fbbs.csdn.net%2Ftopics%2F390543618&amp;ltr=https%3A%2F%2Fwww.baidu.com%2Flink%3Furl%3DPDzLxRpXl7BQhv56h4CSGF5BEdxiiUkaKT4amQsQCJY8N7diO4ncsBKxHlu5b5Oc%26wd%3D%26eqid%3Dbd2ab66a00034cb50000000556700315&amp;ecd=1&amp;psr=1440x900&amp;par=1363x900&amp;pis=-1x-1&amp;ccd=24&amp;cja=true&amp;cmi=8&amp;col=zh-CN&amp;cdo=-1&amp;tcn=1450182256&amp;exps=110207&amp;qn=da0e7723ab7218ca&amp;rdid=1636201&amp;tt=1450182255437.842.1026.1026&amp;feid=110207" width="200" height="22" align="center,center" vspace="0" hspace="0" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" allowtransparency="true" style="box-sizing: content-box; border-width: 0px; border-style: initial; vertical-align: bottom; margin: 0px;"></iframe>
     
  • <iframe id="iframeu1636204_0" src="http://pos.baidu.com/acom?dc=2&amp;di=u1636204&amp;dri=0&amp;dis=0&amp;dai=4&amp;ps=2951x986&amp;dcb=BAIDU_EXP_SSP_define&amp;dtm=BAIDU_DUP_SETJSONADSLOT&amp;dvi=0.0&amp;dci=-1&amp;dpt=none&amp;tsr=1126&amp;tpr=1450182255486&amp;ti=%E5%85%B3%E4%BA%8E%E5%9F%BA%E7%B1%BB%E6%9E%90%E6%9E%84%E5%87%BD%E6%95%B0%E7%9A%84%E9%97%AE%E9%A2%98-CSDN%E8%AE%BA%E5%9D%9B-CSDN.NET-%E4%B8%AD%E5%9B%BD%E6%9C%80%E5%A4%A7%E7%9A%84IT%E6%8A%80%E6%9C%AF%E7%A4%BE%E5%8C%BA&amp;ari=1&amp;dbv=2&amp;drs=1&amp;pcs=1344x815&amp;pss=1344x2988&amp;cfv=0&amp;cpl=6&amp;chi=1&amp;cce=true&amp;cec=UTF-8&amp;tlm=1450182256&amp;ltu=http%3A%2F%2Fbbs.csdn.net%2Ftopics%2F390543618&amp;ltr=https%3A%2F%2Fwww.baidu.com%2Flink%3Furl%3DPDzLxRpXl7BQhv56h4CSGF5BEdxiiUkaKT4amQsQCJY8N7diO4ncsBKxHlu5b5Oc%26wd%3D%26eqid%3Dbd2ab66a00034cb50000000556700315&amp;ecd=1&amp;psr=1440x900&amp;par=1363x900&amp;pis=-1x-1&amp;ccd=24&amp;cja=true&amp;cmi=8&amp;col=zh-CN&amp;cdo=-1&amp;tcn=1450182257&amp;exps=110207&amp;qn=66c5baef1b0d57f2&amp;rdid=1636204&amp;tt=1450182255437.1131.1518.1519&amp;feid=110207" width="200" height="22" align="center,center" vspace="0" hspace="0" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" allowtransparency="true" style="box-sizing: content-box; border-width: 0px; border-style: initial; vertical-align: bottom; margin: 0px;"></iframe>
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
回复次数:9
#1 得分:0回复于: 2013-08-10 12:00:37
来人啊 帮帮忙啊
随时随地逛论坛 论坛移动客户端
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#2 得分:10回复于: 2013-08-10 12:34:19
可以看《深入解析C++对象模型》这本书,说的很详细

用基类指针指向派生类的对象时,指针是通过要读取的内容去判断的,所以他只保留了基类的部分,通过这个指针是无法访问派生类的部分的

在delete的时候,因为基类里面有定义的析构函数,如果派生类中没有定义析构函数,会调用基类的析构函数。即使当派生类中定义了析构函数,C++也会默认把基类的析构函数添加进派生类中已定义的析构函数的。
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#3 得分:10回复于: 2013-08-10 12:49:03
一个类析构时,不管它的基类虚函数是不是virtual的,都是先析构自身再析构基类。这和构造时先构造基类再构造自身的顺序相反。
所以
bear *a = new panda;
delete a;
会输出
animal init...
bear init...
panda init...
bear delete...
animal delete...
如果animal的析构函数是virtual的,就会有动态绑定。输出结果是:
animal init...
bear init...
panda init...
panda delete...
bear delete...
animal delete...
还有虚函数的基本知识:
只要基类函数是virtual的,派生类相应函数不管有没有virtual修饰都是vritual的.
对我有用[1] 丢个板砖[0] 引用 | 举报 | 管理
#4 得分:10回复于: 2013-08-10 12:56:55
引用 2 楼 u011249050 的回复:
可以看《深入解析C++对象模型》这本书,说的很详细

用基类指针指向派生类的对象时,指针是通过要读取的内容去判断的,所以他只保留了基类的部分,通过这个指针是无法访问派生类的部分的

在delete的时候,因为基类里面有定义的析构函数,如果派生类中没有定义析构函数,会调用基类的析构函数。即使当派生类中定义了析构函数,C++也会默认把基类的析构函数添加进派生类中已定义的析构函数的。

2.如果真的是这样,那我bear *a = new panda;delete a的时候,输出的是:
-------------------------------------------------------------------------
bear指针,当然调用bear的析构函数(因为bear的析构不是虚函数),然后再调用bear的基类animal的析构,但是,panda并没有析构。所以这个对象没有完整的析构,不正确。
派生类的析构,自己析构完成一定是会调用他的基类析构的。这和虚函数没关系,虚不虚都要这样一层层往上析构。

3.一般情况下,是不是应该把父类的析构函数都设为虚的?比如我这个例子,除了把animal的析构设为虚之外,也应该把bear的析构设为虚?
--------------------------------------------------
如果要用多态的特性,基类的虚函数必须要虚的,否则析构对象不完整。
不用多态的特性,则不需要虚函数,毕竟虚函数多了个查表的过程,影响些效率。
只要基类某个函数是虚的,所有继承这个基类的函数(包括继承的继承)都是虚的,不写virtual,系统默认也是virtual的
一日为虚,终身为虚。
对我有用[1] 丢个板砖[0] 引用 | 举报 | 管理
#5 得分:0回复于: 2013-08-10 13:02:11
#4 有个小地方手误,更正一下
如果要用多态的特性,基类的虚函数必须要虚的,否则析构对象不完整。
改为 析构函数
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#6 得分:0回复于: 2013-08-10 14:43:12
引用 4 楼 ananluowei 的回复:
Quote: 引用 2 楼 u011249050 的回复:
 可以看《深入解析C++对象模型》这本书,说的很详细

用基类指针指向派生类的对象时,指针是通过要读取的内容去判断的,所以他只保留了基类的部分,通过这个指针是无法访问派生类的部分的

在delete的时候,因为基类里面有定义的析构函数,如果派生类中没有定义析构函数,会调用基类的析构函数。即使当派生类中定义了析构函数,C++也会默认把基类的析构函数添加进派生类中已定义的析构函数的。

2.如果真的是这样,那我bear *a = new panda;delete a的时候,输出的是:
-------------------------------------------------------------------------
bear指针,当然调用bear的析构函数(因为bear的析构不是虚函数),然后再调用bear的基类animal的析构,但是,panda并没有析构。所以这个对象没有完整的析构,不正确。
派生类的析构,自己析构完成一定是会调用他的基类析构的。这和虚函数没关系,虚不虚都要这样一层层往上析构。

3.一般情况下,是不是应该把父类的析构函数都设为虚的?比如我这个例子,除了把animal的析构设为虚之外,也应该把bear的析构设为虚?
--------------------------------------------------
如果要用多态的特性,基类的虚函数必须要虚的,否则析构对象不完整。
不用多态的特性,则不需要虚函数,毕竟虚函数多了个查表的过程,影响些效率。
只要基类某个函数是虚的,所有继承这个基类的函数(包括继承的继承)都是虚的,不写virtual,系统默认也是virtual的
一日为虚,终身为虚。
你说的我大概明白了,还有个问题,析构函数跟合成析构函数的区别是什么?析构派生类对象要先析构派生类,再依次往上析构,我就在想,创建一个派生类对象的时候,编译器是不是在这时候创建一个合成析构函数,然后释放这个派生类对象的时候,用这个合成析构函数一层层的释放,但是有看书上说,这个合成析构函数是用来释放类的成员的,然后就矛盾了,到底析构函数释放哪些东西?合成析构函数又释放哪些东西?
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#7 得分:0回复于: 2013-08-10 14:50:14
引用 6 楼 liyuanhong13 的回复:
你说的我大概明白了,还有个问题,析构函数跟合成析构函数的区别是什么?析构派生类对象要先析构派生类,再依次往上析构,我就在想,创建一个派生类对象的时候,编译器是不是在这时候创建一个合成析构函数,然后释放这个派生类对象的时候,用这个合成析构函数一层层的释放,但是有看书上说,这个合成析构函数是用来释放类的成员的,然后就矛盾了,到底析构函数释放哪些东西?合成析构函数又释放哪些东西?
合成析构函数的概念我不明白。
我看的书c++ prime plus中没有这个名称。
编译器具体怎么实现一层层往上析构的,我也不清楚。
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#8 得分:0回复于: 2013-08-10 21:03:37
引用 6 楼 liyuanhong13 的回复:
Quote: 引用 4 楼 ananluowei 的回复:
Quote: 引用 2 楼 u011249050 的回复:
 可以看《深入解析C++对象模型》这本书,说的很详细

用基类指针指向派生类的对象时,指针是通过要读取的内容去判断的,所以他只保留了基类的部分,通过这个指针是无法访问派生类的部分的

在delete的时候,因为基类里面有定义的析构函数,如果派生类中没有定义析构函数,会调用基类的析构函数。即使当派生类中定义了析构函数,C++也会默认把基类的析构函数添加进派生类中已定义的析构函数的。

2.如果真的是这样,那我bear *a = new panda;delete a的时候,输出的是:
-------------------------------------------------------------------------
bear指针,当然调用bear的析构函数(因为bear的析构不是虚函数),然后再调用bear的基类animal的析构,但是,panda并没有析构。所以这个对象没有完整的析构,不正确。
派生类的析构,自己析构完成一定是会调用他的基类析构的。这和虚函数没关系,虚不虚都要这样一层层往上析构。

3.一般情况下,是不是应该把父类的析构函数都设为虚的?比如我这个例子,除了把animal的析构设为虚之外,也应该把bear的析构设为虚?
--------------------------------------------------
如果要用多态的特性,基类的虚函数必须要虚的,否则析构对象不完整。
不用多态的特性,则不需要虚函数,毕竟虚函数多了个查表的过程,影响些效率。
只要基类某个函数是虚的,所有继承这个基类的函数(包括继承的继承)都是虚的,不写virtual,系统默认也是virtual的
一日为虚,终身为虚。
你说的我大概明白了,还有个问题,析构函数跟合成析构函数的区别是什么?析构派生类对象要先析构派生类,再依次往上析构,我就在想,创建一个派生类对象的时候,编译器是不是在这时候创建一个合成析构函数,然后释放这个派生类对象的时候,用这个合成析构函数一层层的释放,但是有看书上说,这个合成析构函数是用来释放类的成员的,然后就矛盾了东西,到底析构函数释放哪些?合成析构函数又释放哪些东西?

这就涉及到对于类的存储方式的设计了,看《深入解析C++对象模型》没错的
析构函数不自定义的话,只有在必要的时候编译器才会产生。
在C++中基类声明的变量也会在派生类中,申请空间会一起申请,普通函数等是分配内存的地方和变量不一样,虚函数是用一个vtpr指针指向一个虚函数表来实现的。
析构派生类时,你可以想象成,基类的变量也是在派生类中声明的,所以就是先析构派生类中的东西,再析构基类中的。
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#9 得分:10回复于: 2013-08-10 21:15:16
引用 楼主 liyuanhong13 的回复:
有这么几个问题:
1.基类析构函数不为虚的话,用派生类指针转化为基类指针之后,删除这个基类指针时,调用的都只是基类的析构函数,是这样吗?
调用的只是基类的析构函数其实应为:不会调用子类的析构函数。 

C/C++ code
?
1
2
A* p =...
detele p; //A及其基类的析构是会被调用,*p的动态类型的析构函数不会被调用

前提:析构函数非虚
0 0
原创粉丝点击