关于MFC消息映射表与虚函数的效率问题

heiheizh618 2009-08-22 04:20:30
加精
深入浅出MFC对于虚函数实现方式的缺点,它指出:虚函数耗费大量内存,系统最终将被这些额外负担拖垮。

但是现在对于容量巨大的白菜价格的内存来说,这种额外负担是否已经过时了呢~?
书中提到,虚函数表中的每一个项目都是一个函数指针,价值4字节,如果基类的虚函数表有100项 (MFC里面的消息数量是否在这个数量级?),经过十层继承,开支散叶,总共需要耗费多少内存?
我粗略地算了下,不知道这种计算方法是否正确,4Byte*100项=400Byte。如果CCmdTarget中定义100个消息,那其中虚函数表字段大约要占用400字节的内存。
然后我数了下,MFC中派生自CCmdTarget的类总共有100个,那么这么多类的虚函数表总共需要占用多少个字节呢?
400Byte*100个=40KB字节。
然后加上 程序员自己派生的类和消息 所多占的函数指针项,我想也应该不会超过40KB这个基础内存空间的10倍吧?也就是不超过400KB字节的内存。
然而现在动辄几G的内存,这点消耗大概应该可以忽略不计吧?

所以,我的问题是,如果如书上所说,虚函数的实现方案增加了太多的额外负担,那我以上的计算方法哪里出错了?
如果我的上述讨论方法正确的话,那是否能说明书上的说法已经过时,而利用虚函数的实现方法,用空间换取执行效率上的提高可行呢?
...全文
2703 点赞 收藏 95
写回复
95 条回复
切换为时间正序
请发表友善的回复…
发表回复
luck_good 2012-07-08
[Quote=引用 18 楼 的回复:]

别小看虚表占用的字节,如果一个虚表有10个虚函数,那么就是40字节,不大是不是?记住一点,这40字节是每个对象多占用的空间,不是类占用的空间(类占用的空间只有成员函数代码),也就是说一个类没有实例化时,不会浪费任何内存,每创建一个对象,除了对象的数据成员空间外,也需要多分配40字节的虚表空间。2个对象就是80字节,……1000个对象呢?在一个规模不算小的程序中,创建的对象数量可能非常非常多。
……
[/Quote]
不是40,每个对象里是4个字节的vptr指针
回复
aweiname2008 2011-08-28
学习中
应该是优点大于缺点............
回复
[Quote=引用 35 楼 arong1234 的回复:]
我不是很清楚MS设计消息映射的初衷,但是感觉它着眼点更侧重于增加新消息很容易,而不是节省内存。
如果我们使用虚函数机制实现,恐怕对于每个可能的消息我们都必须在基类中定义一个虚函数,而其首要的困难就是你无法猜测未来会出现什么消息,也无法确定需要定义什么样函数原型的虚函数。而使用消息映射,解决这个问题则相对容易,因为这将由未来的程序设计者决定他们的消息该如何处理
[/Quote]

感觉这个原因比较靠谱!
回复
cswuyg 2010-06-13
⊙﹏⊙b汗
1、2、6、18楼对C++的内存模型不了解,都是在乱说。
回复
chenggil10 2010-03-22
你想改变以前人的苦水是不可能吧, 即使他们这样是错误的也是又原因的。现在改天的话代价远远大于你的想象
回复
cfanxue 2010-01-24
学习楼主的钻研精神
回复
cppdow 2010-01-05
learn……
回复
ls2141 2010-01-05
眼睛疼
回复
无为无界 2009-12-20

1,MSDN关于MFC库的TN006(消息数量大,具类之虚函数表有**【逐级】累积效应**;更难的是扩展问题,如M$Window$消息改变和应用程序增加定制消息等不宜于为各个消息建立相应的虚函数-如果定要试一试,那么请先穷举这些未来会增加的而现在还未知的消息再说吧(M$的和无以估量的各种应用的),兄弟们。鄙人才疏学浅无以为计。)
2,Inside C++ Object Model(主要是弟4章)

回复
yujiankk 2009-12-16
18 楼 jameshooo 的回复真是误人子弟啊,不知道对c++有没有真正理解啊
回复
bilt719621 2009-12-15
好帖..
刚跳入VC这坑,暂无发言权...学习了
回复
arkbook 2009-11-16
你这第一种说法肯定错误,学过C++的都知道,一个类一个或者多个虚函数对于内存分配来说是一样的,都是增加了一个long的大小,这个long就是指向了一个vtable,vtable里面才是指向了实际的函数,其中内存分配设计到一个右对齐的问题,看来作者对于虚函数还是没有深刻理解啊,所以一个或者多个虚函数,实际多消耗的只是一个long的字节数,至于你sizeof(自己包含虚函数的class)实际值可能涉及到对齐的问题,不同的编译器给出的结果可能有所区别
回复
rightyeah 2009-08-28
[Quote=引用 80 楼 it_fly 的回复:]

"记住一点,这40字节是每个对象多占用的空间,不是类占用的空间"
这么业余阐述,误人子弟,真不知道你那么多星是怎么来的
[/Quote]嘿嘿,倒分倒出来的
回复
zxc4712 2009-08-26
学习了......
回复
hack002 2009-08-26
学习了赫赫
回复
heiheizh618 2009-08-26
[Quote=引用 78 楼 hiboys 的回复:]
虚表是针对对象实现的还是针对类实现的?
我的想法是针对类的吧
[/Quote]

虚函数表是针对类设计的。
在VS中做个小实验就可以得出来。
1.设计带有虚函数的基类和重写了这个虚函数的派生类。
2.定义对象,并初始化,在初始化后的任意语句设置断点,启动调试
3.在“局部窗口”里面,点开 局部对象 左边的加号,里面第一个成员就是虚函数表,这是编译器为我们加上去的。
4.而这个虚函数表不是整个链表,在最后一列的类型中可以明确看到 这是个指针类型
5.在第二栏里面可以看到 这个指针存储的值


#include<iostream>
using namespace std;

class Base
{
public:
void virtual func1()
{
cout<<"Base function1"<<endl;
}
void virtual func2()
{
cout<<"Base function2"<<endl;
}
};

class Derive:public Base
{
public:
void virtual func1()
{
cout<<"Derive function1"<<endl;
}
};

int main()
{
Base *pObj=new Derive();
return 0;
}


根据以上步骤打开虚函数表,我想大家就会明白了,虚函数表里面存储的是这样的数据.
__vfptr指向const Derive::'vftable'
而Derive::'vftable'里面又是怎么存储的呢?
vftable[0]=Derive::func1(void)
vftable[1]=Base::func2(void)

所以,其实所有的相同类的对象享有的都是同样的虚函数表。
他们的多态行为,其实也是一成不变的---在他们的类类型定义完之后。
通过作用域限定符,就可以明确指出这个类型的对象到底应该调用哪个版本的虚函数了,仅仅加一个类作用域操作符而已。
回复
laowang2 2009-08-26
回复
IT_Fly 2009-08-26
[Quote=引用 18 楼 jameshooo 的回复:]
别小看虚表占用的字节,如果一个虚表有10个虚函数,那么就是40字节,不大是不是?记住一点,这40字节是每个对象多占用的空间,不是类占用的空间(类占用的空间只有成员函数代码),也就是说一个类没有实例化时,不会浪费任何内存,每创建一个对象,除了对象的数据成员空间外,也需要多分配40字节的虚表空间。2个对象就是80字节,……1000个对象呢?在一个规模不算小的程序中,创建的对象数量可能非常非常多。

真正要讨论虚表对内存的浪费程度,应该用虚表大小和结构成员大小的比例来计算,例如类A有1个LONG成员变量和10个虚函数,那么它的对象实例所占用的内存中,有效数据内存是4字节,但虚表开销将是4+4*10=44字节,超级浪费啊,分配的内存大部分都是虚表占用的。
假如类B有10个LONG成员和1个虚函数,有效数据内存是40字节,虚表开销是8字节,显然能够忍受。

侯捷的话在哪都灵验,这是没办法的事情,本质原因就在于虚表是跟随对象走的,而不是类。
[/Quote]
"记住一点,这40字节是每个对象多占用的空间,不是类占用的空间"
这么业余阐述,误人子弟,真不知道你那么多星是怎么来的
回复
IT_Fly 2009-08-26
MFC是n久之前的一个软件框架了,我记得m(n>m)久之前,看过孟岩批判VC的一篇文章就谈过这个问题。
还有也许在一些内存受限的系统开发中(比如wince开发),这个设计还是有些意义的。
回复
hiboys 2009-08-26
虚表是针对对象实现的还是针对类实现的?
我的想法是针对类的吧
回复
发动态
发帖子
VC/MFC
创建于2007-09-28

1.5w+

社区成员

VC/MFC相关问题讨论
申请成为版主
社区公告
暂无公告