虚析构只有一次多态现象?

ken_scott 2014-06-20 05:02:06

#include <iostream>

class B
{
public:
virtual ~B() { std::cout << "B" << std::endl; }
};

class D : public B
{
public:
virtual ~D() { std::cout << "D" << std::endl; }
};


int main(int, char * [])
{
D d;
B & b = d;

b.~B();
std::cout << "---" << std::endl;
b.~B(); // why?
std::cout << "---" << std::endl;

return(0);
}

结果:

D
B
---
B // 这里怎么就没多态了?
---
D
B


虚析构只有一次多态现象?
...全文
175 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
喝水天天 2014-07-23
  • 打赏
  • 举报
回复
class A
{
public:
        void f()
          {}
};
int main() { A * a; a.f(); } 这样的代码在C++中也是允许的,在集合C++的虚函数表你就明白你的问题了。如此大华不缺牛人,我只是一个一年半工作经验的本科生。
喝水天天 2014-07-23
  • 打赏
  • 举报
回复
C++对象在构造的时候会构造一个虚函数表,析构完成了之后,虚函数表就不存在了,所以没有多态了。class A{ public: }
喝水天天 2014-07-23
  • 打赏
  • 举报
回复
自己去研究一下虚函数表,看了你浙江大华的面试经历,我是浙江大华的,大华牛人很多,你还在问这种问题,可见你水平一般。 然后再去C++对象模型,C++调用函数的方式。很明确的说我只有一年半的工作经验。
ken_scott 2014-06-20
  • 打赏
  • 举报
回复
不求甚解者,标准方面的伸手党,发这种帖子,勿怪勿怪。
ken_scott 2014-06-20
  • 打赏
  • 举报
回复
好了,谢谢,结贴
unituniverse2 2014-06-20
  • 打赏
  • 举报
回复
为什么要跟标准而不是跟编译器? 因为编译器遵照标准设计,而不是标准按编译器行为制定。 不跟标准的结果,即使你宣称自己只用你喜欢的厂家的编译器,可能你这次程序能“正常”编译执行的,编译器厂家升级一下编译器你的程序就挂了。如果你说大不了到时候再跟进修改自己的程序,可能连具体位置都不记得了。就算你记得位置,现实中往往会存在的设计依赖性使得一个地方改导致相关的地方都要改然后接着导致更多的地方都需要改。。。这可不是简单的事情了。除非你代码设计成的耦合度极低。但是话说回来,如果平时都无视“遵循标准”这类问题,往往其他诸如“降低耦合度”什么的更不会当回事。
碼上道 2014-06-20
  • 打赏
  • 举报
回复
引用 楼主 ken_scott 的回复:

#include <iostream>

class B
{
public:
    virtual ~B() { std::cout << "B" << std::endl; }
};

class D : public B
{
public:
    virtual ~D() { std::cout << "D" << std::endl; }
};


int main(int, char * [])
{
    D d;
    B & b = d;

    b.~B();
    std::cout << "---" << std::endl;
    b.~B(); // why?
    std::cout << "---" << std::endl;

    return(0);
}
结果:

D
B
---
B  //  这里怎么就没多态了?
---
D
B
虚析构只有一次多态现象?
使用VS跟了一下汇编代码,看到在使用b.B~()后,其实b对D的引用就已经不存在了。 b对象内存的第一个字节被修改了,导致b实际指向了D中基本B内存的部分。我估计一般编译器是这样实现的,但是根本无法保证所有这样,属于不确定的现象。
taodm 2014-06-20
  • 打赏
  • 举报
回复
真好奇,真好学,就先去把《深度探索C++对象模型》啃了。
iamnobody 2014-06-20
  • 打赏
  • 举报
回复
引用 8 楼 ken_scott 的回复:
[quote=引用 7 楼 mingliang1212 的回复:] 不是不合理,是错的. 对的程序才有对的解释,错的程序无需解释.
恩,我也就等等,看有没有独到的(或者深入实现的)解释 [/quote] 唉,中毒太深了.看在你好学的分上再回复一次. 编程不是做实验. 编译器的行为是根据C++标准做出来的.标准就是法律. 现在世界你犯了法,会被依法制裁. C++很多情况下对非法行为的处罚是:行为未定义(意思就是:随便这个程序怎么死吧.编译器爱怎么样都行) 如果真的想进步,那就搜一下"行为未定义". 然后找本好书看.
帅得不敢出门 2014-06-20
  • 打赏
  • 举报
回复
这个你得研究下析构的实现过程了。它可能是修改了vptr
ken_scott 2014-06-20
  • 打赏
  • 举报
回复
引用 9 楼 Automation_dmu 的回复:
研究了下MSVC产生的汇编。 你这段代码里调用了3次析构函数,其中前两次通过虚函数表调用,最后一次直接调用D::~D(); 第一次调用: 通过虚函数表找到了D::~D(),打印“D”, 然后调用B::~B(),打印“B” ; 问题来了,在B::~B()里, 有这么一句
mov	DWORD PTR [eax], OFFSET ??_7B@@6B@
其中eax存放了this指针,??_7B@@6B表示B::`Vftable即B类的虚函数表地址。 显然,这句将D类的虚函数表指针改为了B的虚函数表地址。 所以,第二次调用: 通过B的虚函数索引到的,是B::~B() ,打印“B”; 第三次调用: 直接调用D::~D() ,因为这里没有通过指针或者引用调用,所以编译器直接生成CALL D::~D(); 打印 “D” 和“B”。 GCC和MSVC可以得到相同的结果,析构函数会将虚函数表指针修改为指向其对应类的虚函数表,这点不知道C++标准有没有的条款,LZ有兴趣可以查一下。
谢谢,我差不多就是想看到这种答复
AndyStevens 2014-06-20
  • 打赏
  • 举报
回复
研究了下MSVC产生的汇编。 你这段代码里调用了3次析构函数,其中前两次通过虚函数表调用,最后一次直接调用D::~D(); 第一次调用: 通过虚函数表找到了D::~D(),打印“D”, 然后调用B::~B(),打印“B” ; 问题来了,在B::~B()里, 有这么一句
mov	DWORD PTR [eax], OFFSET ??_7B@@6B@
其中eax存放了this指针,??_7B@@6B表示B::`Vftable即B类的虚函数表地址。 显然,这句将D类的虚函数表指针改为了B的虚函数表地址。 所以,第二次调用: 通过B的虚函数索引到的,是B::~B() ,打印“B”; 第三次调用: 直接调用D::~D() ,因为这里没有通过指针或者引用调用,所以编译器直接生成CALL D::~D(); 打印 “D” 和“B”。 GCC和MSVC可以得到相同的结果,析构函数会将虚函数表指针修改为指向其对应类的虚函数表,这点不知道C++标准有没有的条款,LZ有兴趣可以查一下。
ken_scott 2014-06-20
  • 打赏
  • 举报
回复
引用 7 楼 mingliang1212 的回复:
不是不合理,是错的. 对的程序才有对的解释,错的程序无需解释.
恩,我也就等等,看有没有独到的(或者深入实现的)解释
iamnobody 2014-06-20
  • 打赏
  • 举报
回复
引用 6 楼 ken_scott 的回复:
[quote=引用 5 楼 mingliang1212 的回复:] 这样的程序是错的. 搜一下 : 未定义行为
这样的写法不合理,我也清楚,而且,我对这类问题也是不求甚解的 但,被问倒了,却说不出个所以然, 哎,这个问题是几个星期前问的, 想着,反正我不会这么写,不用管, 但,可恶的是,一直忘不了, 就来问了[/quote] 不是不合理,是错的. 对的程序才有对的解释,错的程序无需解释.
ken_scott 2014-06-20
  • 打赏
  • 举报
回复
引用 5 楼 mingliang1212 的回复:
这样的程序是错的. 搜一下 : 未定义行为
这样的写法不合理,我也清楚,而且,我对这类问题也是不求甚解的 但,被问倒了,却说不出个所以然, 哎,这个问题是几个星期前问的, 想着,反正我不会这么写,不用管, 但,可恶的是,一直忘不了, 就来问了
iamnobody 2014-06-20
  • 打赏
  • 举报
回复
这样的程序是错的. 搜一下 : 未定义行为
ken_scott 2014-06-20
  • 打赏
  • 举报
回复
难道到析构后,虚函数表的指针会自动偏移?搞不清楚
ken_scott 2014-06-20
  • 打赏
  • 举报
回复
引用 2 楼 zmlovelx 的回复:
代码为什么要这么写呢 栈上的就让其自动出栈销毁 new出来的就用delete
这个是,之前同事问我的,他给的程序是:

int main(int, char * [])
{
    B * b = new D;

    b->~B();
    std::cout << "---" << std::endl;
    delete b; // why?
    std::cout << "---" << std::endl;

    return(0);
}
我解释不清楚为什么第二次的多态现象没了
帅得不敢出门 2014-06-20
  • 打赏
  • 举报
回复
代码为什么要这么写呢 栈上的就让其自动出栈销毁 new出来的就用delete
ken_scott 2014-06-20
  • 打赏
  • 举报
回复
希望知道的,讲深入点

64,676

社区成员

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

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