悲剧是如何发生的

0153 2011-07-20 10:16:37
刚看到坛友们,这段代码会内存泄露?这贴,感觉很有讨论价值。

关于类的虚析构函数,举个比较极端的真实的例子:
新手MM写了段程序,如下:
#include "stdafx.h"
class I {
public:
virtual void v() = 0;
};
class B {
public:
B(){}
~B(){}
int m_var;
};
class C : public B, public I {
public:
C(){}
~C(){}
int m_var;
//implement interface I
virtual void v(){}
};
int main(int argc, char* argv[])
{
I* pi = new C;
delete pi;
return 0;
}

运行结果一切正常,但是被上司痛批了一顿,原因是基类B和派生类C的构函数不是虚的。
新手MM还不太明白,于是上司给她的析构函数加上了virtual关键字如下:
#include "stdafx.h"
class I {
public:
virtual void v() = 0;
};
class B {
public:
B(){}
virtual ~B(){}
int m_var;
};
class C : public B, public I {
public:
C(){}
virtual ~C(){}
int m_var;
//implement interface I
virtual void v(){}
};
int main(int argc, char* argv[])
{
I* pi = new C;
delete pi;
return 0;
}

一运行结果出错了,悲剧是如何发生的呢?
在main函数中添加调试信息,打印出各指针,结果一目了然:

int main(int argc, char* argv[])
{
C* pc = new C;
B* pb = pc;
I* pi = pc;
printf("addr(I)=%p, addr(B)=%p, addr(C)=%p\n", pi, pb, pc);
printf("addr(B.var)=%p, addr(C.var)=%p", &pb->m_var, &pc->m_var);
delete pi;
return 0;
}

对于无虚析构函数时,输出如下:
addr(I)=00372960, addr(B)=00372964, addr(C)=00372960
addr(B.var)=00372964, addr(C.var)=00372968

接口I等于类C

添加了虚析构函数后,输出如下:
addr(I)=00372968, addr(B)=00372960, addr(C)=00372960
addr(B.var)=00372964, addr(C.var)=0037296C

接口I不等于类C

结论:
新手MM的程序固然写得漏洞百出,但是编译器会尽最大的可能拯救她,而自作聪明的人由于只记住虚析构而忘记调整B和I的继承前后次序,于是悲剧发生了……
...全文
542 33 打赏 收藏 转发到动态 举报
写回复
用AI写文章
33 条回复
切换为时间正序
请发表友善的回复…
发表回复
horicon 2011-09-18
  • 打赏
  • 举报
回复
运行会出错
horicon 2011-08-02
  • 打赏
  • 举报
回复
第二段代码,上司给她的析构函数加上了virtual关键字后,
我用gcc编译的没有错啊
hacqing 2011-08-01
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 0153 的回复:]
引用 9 楼 tompaz 的回复:

lz干嘛要借什么mm,上司来解说?
又不是写小说


因为是做一个外包项目时遇到的真人真事,接口I是日本客户定义的。而且客户对于我们的代码行数过多提出了指摘,要求把无用的函数都去掉。
[/Quote]

日本人对产品精益求精.
matrixcl 2011-08-01
  • 打赏
  • 举报
回复
[Quote=引用 22 楼 crysleeper 的回复:]

引用 21 楼 bruceteen 的回复:

曾经吧,有个人把#include <stdio.h>写成了#include <std10.h>,因此编译器报一个错
fatal error C1083: Cannot open include file: 'std10.h': No such file or directory
因此我让他先把stdio.h写正确,但他因此而埋怨说“这样一来……
[/Quote]

楼主的leader滥用虚析构函数居然和改正错误拼写是一样的。确实是高见。
ac1998 2011-08-01
  • 打赏
  • 举报
回复
禁止多继承是王道。
lzx3621 2011-08-01
  • 打赏
  • 举报
回复
[Quote=引用 25 楼 teatimel 的回复:]

看见多重继承就火大
[/Quote]
+1,老爹太多辈分很容易混乱
品茶 2011-08-01
  • 打赏
  • 举报
回复
看见多重继承就火大
arithu 2011-08-01
  • 打赏
  • 举报
回复
[Quote=引用 21 楼 bruceteen 的回复:]

曾经吧,有个人把#include <stdio.h>写成了#include <std10.h>,因此编译器报一个错
fatal error C1083: Cannot open include file: 'std10.h': No such file or directory
因此我让他先把stdio.h写正确,但他因此而埋怨说“这样一来,错误更多了,你懂不懂呀?”
我觉得他和你挺像^_……
[/Quote]

相当恰当的例子
hacqing 2011-08-01
  • 打赏
  • 举报
回复
[Quote=引用 21 楼 bruceteen 的回复:]
曾经吧,有个人把#include <stdio.h>写成了#include <std10.h>,因此编译器报一个错
fatal error C1083: Cannot open include file: 'std10.h': No such file or directory
因此我让他先把stdio.h写正确,但他因此而埋怨说“这样一来,错误更多了,你懂不懂呀?”
我觉得他和你挺像^_^……
[/Quote]

有点看不懂呀,错误更多了是个什么情况?求解释.
显示的错误与隐蔽的地雷,确实是前者占优.新手做新手的开发,老鸟做老鸟的开发.如果企业想通过使用新手来做老鸟的开发,从而降低成本,代码质量很明显体现.
zhengwu66 2011-07-25
  • 打赏
  • 举报
回复
没看出什么来。底子还很虚啊。。。补补补
至善者善之敌 2011-07-21
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 shenyan008 的回复:]
有情节的,好久没看了。
[/Quote]


+++1,喜欢带3,不喜欢A
CrySleeper 2011-07-21
  • 打赏
  • 举报
回复
[Quote=引用 21 楼 bruceteen 的回复:]

曾经吧,有个人把#include <stdio.h>写成了#include <std10.h>,因此编译器报一个错
fatal error C1083: Cannot open include file: 'std10.h': No such file or directory
因此我让他先把stdio.h写正确,但他因此而埋怨说“这样一来,错误更多了,你懂不懂呀?”
我觉得他和你挺像^_……
[/Quote]
高见!
bruceteen 2011-07-21
  • 打赏
  • 举报
回复
曾经吧,有个人把#include <stdio.h>写成了#include <std10.h>,因此编译器报一个错
fatal error C1083: Cannot open include file: 'std10.h': No such file or directory
因此我让他先把stdio.h写正确,但他因此而埋怨说“这样一来,错误更多了,你懂不懂呀?”
我觉得他和你挺像^_^

第一段代码虽然在你现在使用的编译器上暂时性的没有运行错误,但它是个隐蔽的大地雷;第二段代码虽然运行出错,但地雷的危害已经被削弱了一半,而且危害失去了隐蔽性。
unituniverse2 2011-07-21
  • 打赏
  • 举报
回复
都是刚学代数的人就想去搞微积分
unituniverse2 2011-07-21
  • 打赏
  • 举报
回复
好样的,
东西还没写出来,时间全花在减行数上了...
qq120848369 2011-07-20
  • 打赏
  • 举报
回复
行为未定义啊,没救了。
Louistao 2011-07-20
  • 打赏
  • 举报
回复
这上司很有喜感啊
0153 2011-07-20
  • 打赏
  • 举报
回复
再发一个续集……
注:由于接口I是客户定的,我们不能做任何修改。
后来接口I被取消了,于是大家都要改程序,MM就来请教我怎么改最省力。
我回答说:“那你把原来接口I里的虚函数全拷贝到基类B里去吧,这样最省力。”
于是MM改写代码如下:(备注:那两个虚析构函数由于确实没有实际作用还丢了上司的脸,已经被上司勒令删除了)
#include "stdafx.h"
class B {
public:
B(){m_var = 0;}
int m_var;
virtual void v() = 0;
};
class C : public B {
public:
C(){m_var = 0;}
int m_var;
virtual void v(){}
};
int main(int argc, char* argv[])
{
B* pb = new C;
delete pb;
return 0;
}

运行结果一切正常,但是又被上司痛批了一顿,新手MM还不太明白,上司骂道:“接口都取消了,纯虚函数还留着有P用,你这么改纯粹是偷懒。”于是就把基类中的纯虚函数声明给删了如下:
#include "stdafx.h"
class B {
public:
B(){m_var = 0;}
int m_var;
};
class C : public B {
public:
C(){m_var = 0;}
int m_var;
virtual void v(){}
};
int main(int argc, char* argv[])
{
B* pb = new C;
delete pb;
return 0;
}

我看了就在边上插嘴道:“那还是请MM把虚析构函数都加上吧,免得又出错了。”,上司又骂:“你懂个P,没用的函数加什么加,被客户指摘说我们膨胀代码行数骗钱你负责?”
上司骂完后果然点了运行,于是悲剧再次发生了……
qq120848369 2011-07-20
  • 打赏
  • 举报
回复
你基类的析构函数virtual ~I(){}都不是虚的,怎么会不出错?
pathuang68 2011-07-20
  • 打赏
  • 举报
回复
楼主的确提到了一个很好的问题。

如果在I中也增加虚析构函数,就不会出现任何问题了。因为I也是基类之一。MM的上司,为什么不给I增加虚析构函数呢?

C作为派生类在这个例子中,其析构函数不必是virtual的。

原则:凡事用作基类的类的析构函数,最好设为virtual的。这样做可以防止很多错误,尽管有时候不是必须要这样做的。
加载更多回复(12)

64,637

社区成员

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

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