析构函数可以是虚函数为什么可以定义为虚函数

15:35 提问
为什么基类的析构函数不是虚函数时,就不会调用派生类的析构函数
我知道,基类的析构函数要生命为虚函数,不然用基类指针删除派生类对象时只会调用基类的析构函数,而不会调用派生类的析构函数,从而发生内存泄露。但是为什么会这样?为什么基类析构函数不是虚函数时,就不会调用派生类析构函数而是虚函数时就会调用派生类析构函数???有没有大神指点一下。
看了几位的回答,都不是我想要的。我已经知道了不声明为虚函数,会造成内存泄露。我想知道为什么会造成内存泄露,原因是什么。
按赞数排序
1. 析构函数跟普通成员没有什么不同,只是编译器在会在特定的时候自动调用析构函数(离开作用域或者执行delete操作);
2. 对于一个成员函数调用(不论是通过对象obj.func还是通过对象指针obj-&func),到底是直接调用还是通过虚函数表调用,在编译的时候是确定的,取决于这个函数是不是虚函数;
3. 综上,如果析构不声明为虚函数,那么delete pBase,就被编译器解释为 Base::~Base,否则被编译器解释为 this-&virtual_function_table[#析构在虚函数表的偏移]
因为虚函数就是做这个的啊。有虚函数的目的,就是为了可以在各级继承之间调用一个合适的方法,而不是直接调用名义上的那个方法。
父类引用指向子类对象,父类构造方法优先于子类,如使用了virtual,父类析构后,还会访问子类未初始化或析构的方法,这样造成程序指向混乱,最终崩溃,在语言设计之初,就规定不能使用这种方式了。
虚函数就是多态的机制,声明为虚函数主要是有一个虚函数表,虚函数表中存储了一些相关的地址,从而知道调用对应的函数。
非虚函数的地址是编译时决定的,其函数调用是直接调用, call $function;
虚函数的地址是运行时决定的,需要查虚函数表,是间接调用 call *%eax
// cat a.cc
struct A {
void f1(void);
void f2(void);
virtual void f3(void);
virtual void f4(void);
void f(struct A *pa)
gcc -S a.cc -o -
# %eax = this
_ZN1A2f1Ev
_ZN1A2f2Ev
(%eax), %eax
# virtual table
(%eax), %eax
# entry #1
(%eax), %eax
# virtual table
# entry #2
(%eax), %eax
多态是虚函数来实现的,没有虚函数,基类指针没法找到子类对应的虚函数地址,就没有办法调用,实现多态
其他相关推荐
其他相似问题析构函数定义为虚函数
已有 3966 次阅读
|个人分类:|系统分类:|关键词:虚函数,析构函数
Base *//inherit 是继承 Base的pb=&c;delete &时需要调用对象的析构函数,如果基类析构不是virtual型,会根据pb的定义类型调用相应类的析构函数,即调用即类析构,但如果你在派生类析构里有内存释放操作,那就会发生内存泄漏。假如基类析构是virtual型,会根据pb所指对象的类型调用相应类的析构函数,即派生类析构,派生类析构再根据析构函数调用的层次原则调用即类析构。这样就保证不会有问题。&Effective C++ (第7条:要将多态基类的析构函数声明为虚函数)这是因为 C++ 做出了这样的规定:当一个派生类对象通过一个指向基类的指针来删除,并且这一基类有一个非虚拟的析构器,此时的结果是不可预知的。通常情况下在运行时,派生类中新派生出的部分得不到销毁。如果一个类不包含虚函数,通常情况下意味着它将不作为基类使用。当一个类不作为基类时,将它的析构其声明为虚拟的通常情况下不是个好主意虚函数的实现需要它所在的对象包含额外的信息,这一信息用来在运行时确定本对象需要调用哪个虚函数。通常,这一信息采取一个指针的形式,这个指针被称为“ vptr ”(“虚函数表指针”)。 vptr 指向一个包含函数指针的数组,这一数组称为“ vtbl ”(“虚函数表”),每个包含虚函数的类都有一个与之相关的 vtbl 。当一个虚函数被一个对象调用时,就用到了该对象的 vptr 所指向的 vtbl ,在 vtbl 中查找一个合适的函数指针,然后调用相应的实函数。需要记住的应该为多态基类声明虚析构器。一旦一个类包含虚函数,它就应该包含一个虚析构器。如果一个类不用作基类或者不需具有多态性,便不应该为它声明虚析构器。一点不明白:为什么基类中析构函数定义为虚函数后,就能执行派生类的析构函数?我猜测是:因为pb=&c的时候,其派生类的虚拟函数表指针会复制给基类pb,因此会调用其派生类的析构函数,然后再调用基类的析构函数。需要说明的是,当基类的析构函数被声明为虚函数后,该基类所有的派生类的析构函数不管是否有virtual关键字修饰,都将自动声明为虚函数。我的理解:有两种情况是要定义虚析构的1.如果基类中有虚函数,否则如所说,用基类指针去析构派生类,会析构不完全。2.如果派生类自定义了operator delete()函数,这时不管基类中是否有没有虚函数都要虚析构。否则会用你不希望的方式去析构。虚函数的内部机制那么,C++中虚函数为什么可以实现上述的动态联编?实际上,虚函数的内部实现机制本身是比较复杂的,且不同的C++编译器的实现方式又都不一样的。不过,大多数C++编译器采用了VTable结构来实现,VTable称为虚表。简单地说,当程序编译时,编译就会为虚函数所在的各个类(基类和派生类)各自创建一个VTable,并将类的虚函数放在此表中,然后在其内部创建一个指针vptr指向该虚表结构VTable,vptr称为虚指针。程序运行时就会根据基类对象所获取的派生类对象将派生类对象的VTable和vptr复制给基类,并由基类来调用,从而实现了类成员函数的动态联编。可见,动态联编时需要指定派生类对象的地址,因而必须通过基类指针或引用对象才能激活虚函数的动态联编机制。需要说明的是:派生类中重写的虚函数应与基类的虚函数完全一样,包括虚函数的返回值类型也应一样。在ANSI/ISO C++中,对于一般函数来说,函数返回值类型是不能作为一般函数的重载区分内容;但对于虚函数而言,函数返回值类型不同的虚函数被认为两个不一样的虚函数。
转载本文请联系原作者获取授权,同时请注明本文来自王秋锋科学网博客。链接地址:
上一篇:下一篇:
当前推荐数:1
评论 ( 个评论)
扫一扫,分享此博文
作者的其他最新博文
热门博文导读
Powered by
Copyright &新手园地& & & 硬件问题Linux系统管理Linux网络问题Linux环境编程Linux桌面系统国产LinuxBSD& & & BSD文档中心AIX& & & 新手入门& & & AIX文档中心& & & 资源下载& & & Power高级应用& & & IBM存储AS400Solaris& & & Solaris文档中心HP-UX& & & HP文档中心SCO UNIX& & & SCO文档中心互操作专区IRIXTru64 UNIXMac OS X门户网站运维集群和高可用服务器应用监控和防护虚拟化技术架构设计行业应用和管理服务器及硬件技术& & & 服务器资源下载云计算& & & 云计算文档中心& & & 云计算业界& & & 云计算资源下载存储备份& & & 存储文档中心& & & 存储业界& & & 存储资源下载& & & Symantec技术交流区安全技术网络技术& & & 网络技术文档中心C/C++& & & GUI编程& & & Functional编程内核源码& & & 内核问题移动开发& & & 移动开发技术资料ShellPerlJava& & & Java文档中心PHP& & & php文档中心Python& & & Python文档中心RubyCPU与编译器嵌入式开发驱动开发Web开发VoIP开发技术MySQL& & & MySQL文档中心SybaseOraclePostgreSQLDB2Informix数据仓库与数据挖掘NoSQL技术IT业界新闻与评论IT职业生涯& & & 猎头招聘IT图书与评论& & & CU技术图书大系& & & Linux书友会二手交易下载共享Linux文档专区IT培训与认证& & & 培训交流& & & 认证培训清茶斋投资理财运动地带快乐数码摄影& & & 摄影器材& & & 摄影比赛专区IT爱车族旅游天下站务交流版主会议室博客SNS站务交流区CU活动专区& & & Power活动专区& & & 拍卖交流区频道交流区
小富即安, 积分 3825, 距离下一级还需 1175 积分
论坛徽章:9
对象销毁的时候,怎么知道是调用哪个虚函数?
是不是有个析构虚函数标志?
&&nbsp|&&nbsp&&nbsp|&&nbsp&&nbsp|&&nbsp&&nbsp|&&nbsp
巨富豪门, 积分 23914, 距离下一级还需 16086 积分
论坛徽章:59
有个ID啊& && && && && &&&,这个要看编译器如何实现
家境小康, 积分 1680, 距离下一级还需 320 积分
论坛徽章:1
每个函数不是都有个名字么!&&在编译后就是对应一个内存地址。
小富即安, 积分 3507, 距离下一级还需 1493 积分
论坛徽章:8
和调用“普通”虚函数的逻辑完全一致。
小富即安, 积分 3825, 距离下一级还需 1175 积分
论坛徽章:9
shan_ghost 发表于
和调用“普通”虚函数的逻辑完全一致。
delete 指向子类对象的父类指针时,调用子类的哪个虚函数
小富即安, 积分 3507, 距离下一级还需 1493 积分
论坛徽章:8
本帖最后由 shan_ghost 于
16:08 编辑
mordorwww 发表于
delete 指向子类对象的父类指针时,调用子类的哪个虚函数
~CLASS_NAME
就好像c++会给普通的函数起个携带参数类型信息的别名一样,所有虚函数也可以得到一个可以是隐含的别名。
对“普通”虚函数,别名可能是 类名::函数名的某种变形,其中类名会被某种算法忽略掉;类似的,析构函数就是 类名::析构函数内部保留名 ,之后就可以用完全相同的方式处理了。
至于具体到某种编译器,肯定有它自己的做法——连不用虚函数表的实现都有。
小富即安, 积分 3825, 距离下一级还需 1175 积分
论坛徽章:9
本帖最后由 mordorwww 于
16:48 编辑
shan_ghost 发表于
~CLASS_NAME
就好像c++会给普通的函数起个携带参数类型信息的别名一样,所有虚函数也可以得到一个可 ...
父类对象的指针,怎么定位到子类对象的 ~CLASS_NAME&&函数
小富即安, 积分 3507, 距离下一级还需 1493 积分
论坛徽章:8博客访问: 462446
博文数量: 139
博客积分: 2221
博客等级: 大尉
技术积分: 1608
注册时间:
do{goodgoodstudy();daydayup();}while(!died)
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: C/C++
在某些类里声明纯虚析构函数很方便。纯虚函数将产生抽象类——不能实例化的类(即不能创建此类型的对象)。有些时候,你想使一个类成为抽象类,但刚好又没有任何纯虚函数。怎么办?因为抽象类是准备被用做基类的,基类必须要有一个虚析构函数,纯虚函数会产生抽象类,所以方法很简单:在想要成为抽象类的类里声明一个纯虚析构函数。 class awov {  public:  virtual ~awov() = 0; // 声明一个纯虚析构函数  };这个类有一个纯虚函数,所以它是抽象的,而且它有一个虚析构函数,所以不会产生析构函数问题。但这里还有一件事:必须提供纯虚析构函数的定义:  awov::~awov() {} // 纯虚析构函数的定义这个定义是必需的,因为虚析构函数工作的方式是:最底层的派生类的析构函数最先被调用,然后各个基类的析构函数被调用。这就是说,即使是抽象类,编译器也要产生对~awov的调用,所以要保证为它提供函数体。如果不这么做,链接器就会检测出来,最后还是得回去把它添上。虽然抽象类的析构函数可以是纯虚函数,但要实例化其派生类对象,仍必须提供抽象基类中析构函数的函数体。抽象类的纯虚函数的实现可以由自身给出,也可以由派生类给出。错了,除了析构函数外,其他函数都不行,必须要在派生类中进行实现。&class a&{&public:&virtual ~a()=0;&};&class b:public a&{&public:&virtual ~b(){};&};&int main()&{&&return 1;&}&析构函数是纯虚函数的亦为抽象类。&上面的例子所以错误,因为b继承a后没有改写a的纯虚函数,因此b也是抽象类,自然不能实例化。&修改如下则正确:&class a&{&public:&virtual ~a()=0;&};&class b:public a&{&public:&virtual ~b(){};&};&a::~a(){};//改写a的纯虚函数,此时a仍为抽象类&int main()&{&&return 1;&}&注意此时a仍为抽象类不能实例化,a aa则错误。
阅读(9650) | 评论(0) | 转发(1) |
相关热门文章
给主人留下些什么吧!~~
请登录后评论。> 问题详情
下面关于构造函数和析构函数的描述中,错误的是()。A.析构函数可以声明为虚函数B.对虚析构函数的调
悬赏:0&答案豆
提问人:匿名网友
发布时间:
下面关于构造函数和析构函数的描述中,错误的是()。A.析构函数可以声明为虚函数B.对虚析构函数的调用可以采用动联编C.一个类的虚函数仅对派生类中重定义的函数起作用,对其他函数没有影响D.构造函数可以声明为虚函数请帮忙给出正确答案和分析,谢谢!
为您推荐的考试题库
网友回答(共1条)展开
D.从实现上看,vbtl在构造函数调用后才建立,因而构造函数不可能成为虚函数从实际含义上看,在调用构造函数时还不能确定对象的真实类型(因为子类会调父类的构造函数);而且构造函数的作用是提供初始化,在对象生命期只执行一次,不是对象的动态行为,也没有太大的必要成为虚函数
您可能感兴趣的试题
1关于友元的概念错误的是(&&)。A.友元函数没有this指针B.调用友元函数时必须在它的实参中给出要访问的对象C.一个类的成员函数也可以作为另一个类的友元函数D.只能在类的公有段声明友元2已知:int a,b;下列switch语句中,(&&)是正确的。A.switdh (a) { case a: a + +;break; case b: b + +;break; }B.switch(a+b) { case 1:a+b;break; case 1:a-b }C.switch(a*a) { case 1,2:+ +a; case 3, 4:+ +b: }D.switch(a/10+b) { cases 5:a/5:break: default:a+b; }3带有虚基类的多层派生类构造函数的成员初始化列表中都要列出虚基类的构造函数,这样将对虚基类的子对象初始化(&&)。A.与虚基类下面的派生类个数有关B.多次C.两次D.一次4已知类X成功地重载了- -、=、+、和[]这几个运算符,则其中肯定属于成员函数的运算符是(&&)。A.+和=B.[]和后置--C.=和[]D.前置- -和[]
我有更好的答案
请先输入下方的验证码查看最佳答案
图形验证:
验证码提交中……
找答案会员
享三项特权
找答案会员
享三项特权
找答案会员
享三项特权
选择支付方式:
支付宝付款
郑重提醒:支付后,系统自动为您完成注册
请使用微信扫码支付(元)
支付后,系统自动为您完成注册
遇到问题请联系在线客服QQ:
请您不要关闭此页面,支付完成后点击支付完成按钮
遇到问题请联系在线客服QQ:
恭喜您!升级VIP会员成功
常用邮箱:
用于找回密码
确认密码:}

我要回帖

更多关于 析构函数调用虚函数 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信