为什么析构函数里不能调用虚函数

liulin2016 2013-08-05 09:32:24
构造函数式因为虚表没有初始化完成

那析构函数为什么也不能调用?

啥原因?

有例子解释吗?
...全文
1200 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
tcmakebest 2013-08-11
  • 打赏
  • 举报
回复
引用 17 楼 halleyzhang3 的回复:
如果在父类析构函数中调用虚函数,其实际调用的是子类的函数,而此时子类析构函数已经把数据销毁了,会出现不可知的结果。
与我的想法一致!
halleyzhang3 2013-08-07
  • 打赏
  • 举报
回复
如果在父类析构函数中调用虚函数,其实际调用的是子类的函数,而此时子类析构函数已经把数据销毁了,会出现不可知的结果。
lm_whales 2013-08-07
  • 打赏
  • 举报
回复
引用 14 楼 liulin2016 的回复:
[quote=引用 10 楼 lm_whales 的回复:] C++ 已经避免了这种野指针问题了,我说的是,假如允许的话。

#include<iostream>
#include<typeinfo>
using namespace std;

class A{
char *Aname ;
    A(){
    Aname =new [strlen(typeid(*this).name())+1]; 
    strcpy(Aname,typeid(*this).name());
};

virtual void print(){cout<<Aname<<endl;};
~A(){
   print();//如果允许正常,调用虚函数,此时 this->Bname 就是野指针。
            //非虚函数,是不存在的这个问题的
            //C++编译器,经过处理,调用虚函数和非虚函数结果基本一致,避免了野指针的问题。
            //但是也因此,造成此处虚函数的调用,和其他时候调用虚函数;
            //效果不一致,达不到多态的效果了。
            //所以才有,析构函数,不要调用虚函数的说法。
   delete []Aname; 
};
};
class B()
{
 char *Bname
  B(){
  Bname =new [strlen(typeid(*this).name())+1]; 
  strcpy(Bname,typeid(*this).name());
  }
    void print()const{
    cout<<Bname<<endl;//这里只有读操作,效果还不严重,如果写入数据,那就会严重得多了。
     //所以,C++ 中,编译器,处理了虚函数,在析构函数中的调用,解决掉相当一大部分的问题。
      
   };
 
~ B(){
  print();
  delete []Bname;//在此以后,Bname称为野指针。
  //~B()结束后,会自动调用 ~A() 函数,如果此时,还正常调用虚函数
  //就和在这里调用 print(); 效果一致了   

};
};

int main(){

A * pa=new A();

A *pb =new B();
delete pb;

delete pa; 
return 0;
}
无法编译, 亲,不知道你从什么地方抄来的代码 name() 是什么? [/quote] 这不过,随手写的一个,示范一下而已,C++已经处理了,构造函数,和析构函数调用虚函数的问题了。 构造函数,和析构函数,调用虚函数,和调用非虚函数,结果一致。达不到多态的目的。 虚函数的主要作用,就是实现面向对象的多态。 即通过父类指针,调用一个函数,指向不同类型(主要是指子类)对象的指针,调用不同类型定义的虚函数 。 虚函数的调用和指针的声明类型无关,只和所指对象的实际类型有关。 引用和指针相似,都是C++面向对象,实现多态的方式。 父类指针,指向子类对象(也可以是子类的子类。。。。),才是真正的多态行为。 引用和指针做法一致。 父类指针指向父类对象,子类指针指向子类对象,都不算是多态。 只是采用用多态的类似手法(虚函数),调用函数而已。
hydys 2013-08-07
  • 打赏
  • 举报
回复
拆房子时,父类提供的房梁,被子类当做自己的先拆了,你还要拆,而这个空间有可能分配出去了,结果你有可能拆到别人家的梁
liulin2016 2013-08-07
  • 打赏
  • 举报
回复
引用 10 楼 lm_whales 的回复:
C++ 已经避免了这种野指针问题了,我说的是,假如允许的话。

class A{
char *Aname ;
    A(){
    Aname =new [strlen(typeid(*this).name())+1]; 
}

virtual void print(){cout<<Aname<<endl;};
~A(){
   print();//如果允许正常,调用虚函数,此时 this->Bname 就是野指针。
            //非虚函数,是不存在的这个问题的
            //C++编译器,经过处理,调用虚函数和非虚函数结果基本一致,避免了野指针的问题。
            //但是也因此,造成此处虚函数的调用,和其他时候调用虚函数;
            //效果不一致,达不到多态的效果了。
            //所以才有,析构函数,不要调用虚函数的说法。
   delete []Aname; 
};
};
class B()
{
 char *Bname
  B(){
  Bname =new [strlen(typeid(*this).name())+1]; 
  strcpy( 
  }
    void print()const{
    cout<<Bname<<endl;//这里只有读操作,效果还不严重,如果写入数据,那就会严重得多了。
     //所以,C++ 中,编译器,处理了虚函数,在析构函数中的调用,解决掉相当一大部分的问题。
      
   };
 
~ B(){
  print();
  delete []Bname;//在此以后,Bname称为野指针。
  //~B()结束后,会自动调用 ~A() 函数,如果此时,还正常调用虚函数
  //就和在这里调用 print(); 效果一致了   

};
};

int main(){

A * pa=new A();

A *pb =new B();
delete pb;

delete pa; 
return 0;
}
无法编译, 亲,不知道你从什么地方抄来的代码 name() 是什么?
赵4老师 2013-08-05
  • 打赏
  • 举报
回复
计算机组成原理→DOS命令→汇编语言→C语言(不包括C++)、代码书写规范→数据结构、编译原理、操作系统→计算机网络、数据库原理、正则表达式→其它语言(包括C++)、架构…… 对学习编程者的忠告: 眼过千遍不如手过一遍! 书看千行不如手敲一行! 手敲千行不如单步一行! 单步源代码千行不如单步对应汇编一行! 单步类的实例“构造”或“复制”或“作为函数参数”或“作为函数返回值返回”或“参加各种运算”或“退出作用域”的语句对应的汇编代码几步后,就会来到该类的“构造函数”或“复制构造函数”或“运算符重载”或“析构函数”对应的C/C++源代码处。 VC调试时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化,这样过一遍不就啥都明白了吗。 对VC来说,所谓‘调试时’就是编译连接通过以后,按F10或F11键单步执行一步以后的时候,或者在某行按F9设了断点后按F5执行停在该断点处的时候。
lm_whales 2013-08-05
  • 打赏
  • 举报
回复
析构函数里面可以调用虚函数,但是结果并不是多态所要的那种。 而且析构函数,并不需要以多态的方式,调用虚函数。 同时这里说的调用虚函数; 是指显式或者隐式通过 this指针,直接调用虚函数。 不包括间接调用,也不包括调用其他类的虚函数。
lm_whales 2013-08-05
  • 打赏
  • 举报
回复
把对象当作房子 父类对象就相当与房子的一部分构件了,其他构建就是子类的成员变量。 构造函数,是一步一个脚印的,打造子类对象,先创建,父类对象,再创建其他成员变量对象。子类对象这种房子,就搭建起来了。 析构函数,正相反,是一步一步的在拆房子,先拆除子类的成员变量,然后才拆除父对象,当子类的所有成员变量拆除完成,才开始拆除父类对象。 在虚函数里,如果像正常的虚函数调用一样,调用虚函数,就可能会调用那些已经析构的的子类对象的成员变量,结果未定义,如果其中有些是指针,并且是动态分配内存的,此时内存已经释放,这些指针就都是个野指针,访问野指针程序很可能崩溃。 所以析构函数,不能通过 this 指针,正常调用虚函数。 编译器的做法是,析构子类完成后,恢复父类对象的的虚函数表,这时子类对象对应的父类对象的虚函数表,已经是父类的虚函数表,此时调用虚函数,就不在和正常虚函数调用一样了,父类对象只能调用自己的虚函数,非虚函数调用方式并无不同。 虚函数的实现方式是,父类和子类的虚函数表指针,占用同一位置, 父类和子类的同名,同参并且签名相同的虚函数,在虚函数表里占用同一位置。 多继承的子类有多份不同类型的父类对象,继承的每一个父类对象和子类的同参并且签名相同的虚函数占有同一个位置。 只要找到父类对象的位置(通过类型转换和指针赋值,指针初始化,引用初始化 ) 就能够通过父类的指针,父类的引用,调用子类的虚函数。只是一个查表操作,没有任何其他技巧。
zyx040404 2013-08-05
  • 打赏
  • 举报
回复
在类设计时,析构函数一般都设计成虚函数
Night_Fate 2013-08-05
  • 打赏
  • 举报
回复
会调用基类的方法
max_min_ 2013-08-05
  • 打赏
  • 举报
回复
不要在类的构造或者析构函数中调用虚函数,因为这种调用不会如你所愿,即使成功一点,最后还会使你沮丧不已。  不要在类的构造或者析构过程中调用虚函数,因为这样的调用永远不会沿类继承树往下传递到子类中去 至于例子: http://hi.baidu.com/richiechyi/item/0f0fe3391443e389f5e4ad91
和道一文字JC 2013-08-05
  • 打赏
  • 举报
回复
构造函数跟析构函数里面都可以调用虚函数,编译器不会报错! 但是由于类的构造次序是由基类到派生类,所以在构造函数中调用虚函数,这个虚函数不会呈现出多态 相反,类的析构是从派生类到基类,当调用继承层次中某一层次的类的析构函数时往往意味着其派生类部分已经析构掉,所以也不会呈现出多态。(因此如果在基类中声明的是纯虚函数并且在基类的析构函数中调用之编译器会报错)
ri_aje 2013-08-05
  • 打赏
  • 举报
回复
析构函数里面可以调用虚函数,只不过行为和其他地方调用的时候不太一样。
漫步者、 2013-08-05
  • 打赏
  • 举报
回复
你在析构里面去调用方法,你疯了吧,加入在尾部加入你的行为,你调用方法,会导致一切的未知。
mujiok2003 2013-08-05
  • 打赏
  • 举报
回复
C++动态绑定机制仅适用于完全构造的对象,构造和析构函数一样,对象不完整。 如果在部分构造的函数对象上动态绑定,行为很怪异(与实现有关)。 你不希望维护无法预测行为的产品吧?
qq120848369 2013-08-05
  • 打赏
  • 举报
回复
因为虚函数是向派生类查找的,构造基类的时候派生类已经构造完了,所以是可以在构造函数里调虚函数的,能够在派生类里找得到。 析构基类的时候,派生类已经析构了,基类的析构里调虚函数已经找不到派生类了,自然编译报错。
lm_whales 2013-08-05
  • 打赏
  • 举报
回复
C++ 已经避免了这种野指针问题了,我说的是,假如允许的话。

class A{
char *Aname ;
    A(){
    Aname =new [strlen(typeid(*this).name())+1]; 
}

virtual void print(){cout<<Aname<<endl;};
~A(){
   print();//如果允许正常,调用虚函数,此时 this->Bname 就是野指针。
            //非虚函数,是不存在的这个问题的
            //C++编译器,经过处理,调用虚函数和非虚函数结果基本一致,避免了野指针的问题。
            //但是也因此,造成此处虚函数的调用,和其他时候调用虚函数;
            //效果不一致,达不到多态的效果了。
            //所以才有,析构函数,不要调用虚函数的说法。
   delete []Aname; 
};
};
class B()
{
 char *Bname
  B(){
  Bname =new [strlen(typeid(*this).name())+1]; 
  strcpy( 
  }
    void print()const{
    cout<<Bname<<endl;//这里只有读操作,效果还不严重,如果写入数据,那就会严重得多了。
     //所以,C++ 中,编译器,处理了虚函数,在析构函数中的调用,解决掉相当一大部分的问题。
      
   };
 
~ B(){
  print();
  delete []Bname;//在此以后,Bname称为野指针。
  //~B()结束后,会自动调用 ~A() 函数,如果此时,还正常调用虚函数
  //就和在这里调用 print(); 效果一致了   

};
};

int main(){

A * pa=new A();

A *pb =new B();
delete pb;

delete pa; 
return 0;
}
liulin2016 2013-08-05
  • 打赏
  • 举报
回复
引用 6 楼 lm_whales 的回复:
把对象当作房子 父类对象就相当与房子的一部分构件了,其他构建就是子类的成员变量。 构造函数,是一步一个脚印的,打造子类对象,先创建,父类对象,再创建其他成员变量对象。子类对象这种房子,就搭建起来了。 析构函数,正相反,是一步一步的在拆房子,先拆除子类的成员变量,然后才拆除父对象,当子类的所有成员变量拆除完成,才开始拆除父类对象。 在虚函数里,如果像正常的虚函数调用一样,调用虚函数,就可能会调用那些已经析构的的子类对象的成员变量,结果未定义,如果其中有些是指针,并且是动态分配内存的,此时内存已经释放,这些指针就都是个野指针,访问野指针程序很可能崩溃。 所以析构函数,不能通过 this 指针,正常调用虚函数。 编译器的做法是,析构子类完成后,恢复父类对象的的虚函数表,这时子类对象对应的父类对象的虚函数表,已经是父类的虚函数表,此时调用虚函数,就不在和正常虚函数调用一样了,父类对象只能调用自己的虚函数,非虚函数调用方式并无不同。 虚函数的实现方式是,父类和子类的虚函数表指针,占用同一位置, 父类和子类的同名,同参并且签名相同的虚函数,在虚函数表里占用同一位置。 多继承的子类有多份不同类型的父类对象,继承的每一个父类对象和子类的同参并且签名相同的虚函数占有同一个位置。 只要找到父类对象的位置(通过类型转换和指针赋值,指针初始化,引用初始化 ) 就能够通过父类的指针,父类的引用,调用子类的虚函数。只是一个查表操作,没有任何其他技巧。
你说的虚函数野指针的问题, 可否给个简单的例子看看 尽管这些话,我听了无数遍了,但是不见代码,依然没有深刻的体会 谢谢

64,665

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧