关于MFC消息映射表与虚函数的效率有关问题
关于MFC消息映射表与虚函数的效率问题
深入浅出MFC对于虚函数实现方式的缺点,它指出:虚函数耗费大量内存,系统最终将被这些额外负担拖垮。
但是现在对于容量巨大的白菜价格的内存来说,这种额外负担是否已经过时了呢~?
书中提到,虚函数表中的每一个项目都是一个函数指针,价值4字节,如果基类的虚函数表有100项 (MFC里面的消息数量是否在这个数量级?),经过十层继承,开支散叶,总共需要耗费多少内存?
我粗略地算了下,不知道这种计算方法是否正确,4Byte*100项=400Byte。如果CCmdTarget中定义100个消息,那其中虚函数表字段大约要占用400字节的内存。
然后我数了下,MFC中派生自CCmdTarget的类总共有100个,那么这么多类的虚函数表总共需要占用多少个字节呢?
400Byte*100个=40KB字节。
然后加上 程序员自己派生的类和消息 所多占的函数指针项,我想也应该不会超过40KB这个基础内存空间的10倍吧?也就是不超过400KB字节的内存。
然而现在动辄几G的内存,这点消耗大概应该可以忽略不计吧?
所以,我的问题是,如果如书上所说,虚函数的实现方案增加了太多的额外负担,那我以上的计算方法哪里出错了?
如果我的上述讨论方法正确的话,那是否能说明书上的说法已经过时,而利用虚函数的实现方法,用空间换取执行效率上的提高可行呢?
------解决方案--------------------
楼主的想法是很好的,我觉得候捷的说法也有点言过其实,用虚函数来实现消息处理,Delphi和;C++Builder本身就是这么做的,所以个人认为完全可行。而且显然Delphi的VCL用的技术要比MFC先进,关于Delphi的处
理机制可以看一下李维的《inside vcl》(即 深入核心——vcl架构剖析)。
虚函数表应该不会是简单的4Byte的函数指针这么简单,这个不是很清楚。
别外我觉得 每一个类对象比他可见的字节多一个虚函数表的大小 并不是指每一个对像都要对应一个虚函数表
------解决方案--------------------
楼主说的没错。
而且你的程序中不太可能把MFC中全部的类都用到。对于那些没有用到的类,当然就不会需要它们所需的虚表空间(即使是选择动态连接MFC库,那也是各个使用MFC的程序共享系统中同一份mfcxx.dll的物理拷贝——映射到各个进程自己的地址空间而已)。
至于说每个对象都要浪费4字节的指向虚表的指针,这个是没有办法的,这是实现多态的代价(这里特指运行时多态)。任何一种语言,只要它想从语言的级别上支持多态,所需要的代价都不太可能比这个更低。如果你用C语言模拟一下多态,就会对这一点更清楚,空间开销不太可能比这更少。
另外,拥有多态行为的对象,往往都是一些负责上层业务逻辑的对象,这样的对象通常不会在程序中大量存在,既使数量稍微多一些,可能每一个对象的生存期也不会很长。它们不会像CString那样,可能需要在程序中长时间大量存在,以致于空间的开销可能造成不可忽略的浪费,但问题是像CString这样的功能具体的util类,通常是不需要有多态行为的,也就不需要有虚函数。
另外,在JAVA中,除了静态函数和private函数之外,所有的类的成员函数默认就是virtual的。但JAVA不也照样构造了许多大型的企业级应用么。
------解决方案--------------------
所以,如果要在虚函数方面评论一下MFC的设计,不应该把着眼点放在虚函数的空间开销方面。而应该想:MFC中有没有什么东西在不需要使用虚函数的时候却使用了虚函数。如果没有,那就是没啥问题的。
写代码不都是为了解决某种需求么,所以只要别在不需要的时候使用就OK。
------解决方案--------------------
虽然基类有几百个虚函数是太夸张了些。
不过以现在的硬件水平,这点内存消耗不算什么了,写个.net的小程序,什么都没干呢,
看看内存消耗,加起来已经过20兆了。
------解决方案--------------------
.net框架的DLL是各个进程共享的,MFC的也一样,除非静态链接。
MFC的消息映射还有对最近收到的消息的映射做缓存,这点用switch和虚函数表做不到。
注意消息映射不光是有调用消息处理函数,还有一个消息分发的过程。
------解决方案--------------------
我个人的机器4G内存,工作机器64G,除非定义对象的个数真的达到那么海量的情况下,4字节每对象的内存损耗才需要费劲去思考,但是这种情况毕竟是很少的。
况且,映射表方式省内存的前提是你海量类型、对象都没有虚表,也就是每个类都不允许虚函数,这即使在MFC中也是不可能的。最基本的,如果一个类希望由基类指针来删除,虚析构函数是不可避免的。从这点上来讲,把“映射表”和“虚函数”对立本来就是一个假命题,至少在MFC中,所有在message map中涉及的类都是用了虚函数的,使用“映射表”根本没有节省所谓虚表所省下来的内存,因为虚表总还是定义了。
------解决方案--------------------
虚函数的个数对这个问题几乎是没有贡献的,因为每个“类”是一个虚表,几百个虚函数最多也就多占有最多k级的内存而已,几乎毫无消耗
------解决方案--------------------
------解决方案--------------------
------解决方案--------------------
深入浅出MFC对于虚函数实现方式的缺点,它指出:虚函数耗费大量内存,系统最终将被这些额外负担拖垮。
但是现在对于容量巨大的白菜价格的内存来说,这种额外负担是否已经过时了呢~?
书中提到,虚函数表中的每一个项目都是一个函数指针,价值4字节,如果基类的虚函数表有100项 (MFC里面的消息数量是否在这个数量级?),经过十层继承,开支散叶,总共需要耗费多少内存?
我粗略地算了下,不知道这种计算方法是否正确,4Byte*100项=400Byte。如果CCmdTarget中定义100个消息,那其中虚函数表字段大约要占用400字节的内存。
然后我数了下,MFC中派生自CCmdTarget的类总共有100个,那么这么多类的虚函数表总共需要占用多少个字节呢?
400Byte*100个=40KB字节。
然后加上 程序员自己派生的类和消息 所多占的函数指针项,我想也应该不会超过40KB这个基础内存空间的10倍吧?也就是不超过400KB字节的内存。
然而现在动辄几G的内存,这点消耗大概应该可以忽略不计吧?
所以,我的问题是,如果如书上所说,虚函数的实现方案增加了太多的额外负担,那我以上的计算方法哪里出错了?
如果我的上述讨论方法正确的话,那是否能说明书上的说法已经过时,而利用虚函数的实现方法,用空间换取执行效率上的提高可行呢?
------解决方案--------------------
楼主的想法是很好的,我觉得候捷的说法也有点言过其实,用虚函数来实现消息处理,Delphi和;C++Builder本身就是这么做的,所以个人认为完全可行。而且显然Delphi的VCL用的技术要比MFC先进,关于Delphi的处
理机制可以看一下李维的《inside vcl》(即 深入核心——vcl架构剖析)。
虚函数表应该不会是简单的4Byte的函数指针这么简单,这个不是很清楚。
别外我觉得 每一个类对象比他可见的字节多一个虚函数表的大小 并不是指每一个对像都要对应一个虚函数表
------解决方案--------------------
楼主说的没错。
而且你的程序中不太可能把MFC中全部的类都用到。对于那些没有用到的类,当然就不会需要它们所需的虚表空间(即使是选择动态连接MFC库,那也是各个使用MFC的程序共享系统中同一份mfcxx.dll的物理拷贝——映射到各个进程自己的地址空间而已)。
至于说每个对象都要浪费4字节的指向虚表的指针,这个是没有办法的,这是实现多态的代价(这里特指运行时多态)。任何一种语言,只要它想从语言的级别上支持多态,所需要的代价都不太可能比这个更低。如果你用C语言模拟一下多态,就会对这一点更清楚,空间开销不太可能比这更少。
另外,拥有多态行为的对象,往往都是一些负责上层业务逻辑的对象,这样的对象通常不会在程序中大量存在,既使数量稍微多一些,可能每一个对象的生存期也不会很长。它们不会像CString那样,可能需要在程序中长时间大量存在,以致于空间的开销可能造成不可忽略的浪费,但问题是像CString这样的功能具体的util类,通常是不需要有多态行为的,也就不需要有虚函数。
另外,在JAVA中,除了静态函数和private函数之外,所有的类的成员函数默认就是virtual的。但JAVA不也照样构造了许多大型的企业级应用么。
------解决方案--------------------
所以,如果要在虚函数方面评论一下MFC的设计,不应该把着眼点放在虚函数的空间开销方面。而应该想:MFC中有没有什么东西在不需要使用虚函数的时候却使用了虚函数。如果没有,那就是没啥问题的。
写代码不都是为了解决某种需求么,所以只要别在不需要的时候使用就OK。
------解决方案--------------------
虽然基类有几百个虚函数是太夸张了些。
不过以现在的硬件水平,这点内存消耗不算什么了,写个.net的小程序,什么都没干呢,
看看内存消耗,加起来已经过20兆了。
------解决方案--------------------
.net框架的DLL是各个进程共享的,MFC的也一样,除非静态链接。
MFC的消息映射还有对最近收到的消息的映射做缓存,这点用switch和虚函数表做不到。
注意消息映射不光是有调用消息处理函数,还有一个消息分发的过程。
------解决方案--------------------
我个人的机器4G内存,工作机器64G,除非定义对象的个数真的达到那么海量的情况下,4字节每对象的内存损耗才需要费劲去思考,但是这种情况毕竟是很少的。
况且,映射表方式省内存的前提是你海量类型、对象都没有虚表,也就是每个类都不允许虚函数,这即使在MFC中也是不可能的。最基本的,如果一个类希望由基类指针来删除,虚析构函数是不可避免的。从这点上来讲,把“映射表”和“虚函数”对立本来就是一个假命题,至少在MFC中,所有在message map中涉及的类都是用了虚函数的,使用“映射表”根本没有节省所谓虚表所省下来的内存,因为虚表总还是定义了。
------解决方案--------------------
虚函数的个数对这个问题几乎是没有贡献的,因为每个“类”是一个虚表,几百个虚函数最多也就多占有最多k级的内存而已,几乎毫无消耗
------解决方案--------------------
------解决方案--------------------
------解决方案--------------------