免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 14040 | 回复: 61
打印 上一主题 下一主题

我所理解的 c++虚函数 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2012-06-16 14:10 |只看该作者 |倒序浏览
本帖最后由 titer1 于 2012-06-16 16:40 编辑

我理解的虚函数

曾经在微博看到大牛说道,面试c++能力,虚函数能摆谈一个小时就是不错的。

先列出问题。
1. c++中的关键词角度

Virtual 函数,纯虚函数,虚继承 。

2. 面向c++对象模型角度 看待虚函数

3. 虚函数的使用角度
        结合ctor ,dtor等等

4. 高级主题,简要说明虚函数的优点、缺点

5.高级主题,请举例说明虚函数性能低的情形,并给出解决方案。



这里,我的回答是:

1. c++中的关键词角度

Virtual 函数(多态的基础),纯虚函数(对应类为抽象类),虚继承 (使用于多重继承对应父类为虚基类)。


2. 面向c++对象模型角度 看待虚函数
首先要懂虚表,(基础是分开一般函数(没有使用virtual关键词修饰的)的布局,还有知道dtor位于虚表)
在不同的继承层次(线性,扇形,钻石型)知道,虚表的布局。
还有使用了多态的函数如何转换成 c语言 (我理解:先找到虚表,然后找到对应函数指针)


3. 虚函数的使用角度
ctor为什么不能够调用虚函数(我理解:所有的子类成员还没有初始化,此时访问时机不对。)
dtor为什么要设置为虚函数(调用子类dtor一次,就逐层销毁了所有(父类的,子类的)资源 )


4. 高级主题,简要说明虚函数的优点、缺点(答案详细参考引用文档1,2)
优点:我理解,就是多态
缺点:这里多说点
a.
比起编译时就确定地址的同类功能的函数调用, 动态连编时有效率损失.
b.
从指令级别讲,
     (1.虚函数的不确定性,会让编译器的 指令预测(或者说流水线技术)失效(详细参考)
     (2. 多了几条汇编指令(运行时得到对应类的函数的地址)
     (3. 编译器不能内联优化(仅在用父类引用或者指针调用时,不能内联)

       
       
5. 高级主题,请举例说明虚函数性能低的情形,并给出解决方案。(答案详细参考引用文档1,3)
       
当虚函数位于多层循环嵌套的核心部分时,这种情况更严重。还有另外一种性能下降显著的情况:当一系列虚函数频繁被调用时,我们不得不支付程序多次穿过“虚边界”的消耗。
               
解决方法很简单:把抽象提高到更高层即可。总的思路是不要让程序频繁在“虚-虚”或者“虚-非虚”之间切换。抽象提高后,把一系列虚函数或者循环中的虚函数都转移到更高一层,大大减低了切换的次数。
       
       
更多的关于虚函数的效率
详细点,效率有损失,数量级到底有多少,曾经有网友测试,1亿次系统调用会损失约1s,我个人觉得有点微不足道哈。但是这个测试案例不一定有代表性。

a.
虚函数调用效率和继承层数无关;
b.
其实虚函数还是挺快的。虚函数的效率到底低不低和实际要调用的函数的耗时有关,当函数本身的的耗时越长,则虚函数的影响则越小。
c.
如果真的要完全移除虚函数,那么如果要实现运行时多态,则要用到函数指针,据上面的分析,函数指针基本具有虚函数的所有缼点(要传递函数指针,同样无法内联,同样影响流水线),且函数指针会使代码混乱。

补充:
幻の上帝 网友 提出使用 CRTP模式 解决效率问题
shanehan 网友提出 使用 boost::bind boost::function来替代虚函数


参考文档:
        1. c++ 虚函数机制效率问题(关于流水线)
         <http://blog.csdn.net/metalkittie/article/details/3281916>
        2. C++中虚函数(virtual function)到底有多慢
         <http://blog.csdn.net/hengyunabc/article/details/7461919>
        3. 【C++】虚函数的性能和vtable的细节——《C++游戏编程》读书笔记1
         <http://hi.baidu.com/springlie/bl ... 2a7f15b21bba55.html>
        4.[C++虚函数系列1]如何使用CRTP模式解决虚函数的效率损失问题? (
        http://blog.chinaunix.net/uid-9605822-id-2000053.html

论坛徽章:
0
2 [报告]
发表于 2012-06-16 14:11 |只看该作者
希望大家看看小弟 在哪些还有不完善的?)

论坛徽章:
59
2015年亚洲杯之约旦
日期:2015-01-27 21:27:392015年亚洲杯之日本
日期:2015-02-06 22:09:41拜羊年徽章
日期:2015-03-03 16:15:432015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:50:282015元宵节徽章
日期:2015-03-06 15:50:392015年亚洲杯之阿联酋
日期:2015-03-19 17:39:302015年亚洲杯之中国
日期:2015-03-23 18:52:23巳蛇
日期:2014-12-14 22:44:03双子座
日期:2014-12-10 21:39:16处女座
日期:2014-12-02 08:03:17天蝎座
日期:2014-07-21 19:08:47
3 [报告]
发表于 2012-06-16 14:25 |只看该作者
回复 2# titer1


    very good

but
ctor为什么不能够调用虚函数(我理解:子类ctor调用时先调用父类的,但是子类虚函数现在地址还没有在父类ctor中确定,两者矛盾)

seems is a misunderstand.

see:
我理解:子类ctor调用时先调用父类的

it is right
但是子类虚函数现在地址还没有在父类ctor中确定,两者矛盾

it is wrong: teh reason may be that the data of subclass are not ready in parent's constructor function.
so if the virtual function (which imped in the subclass) be called, it will access the wrong data.

wish helps.

论坛徽章:
14
巨蟹座
日期:2013-11-19 14:09:4615-16赛季CBA联赛之青岛
日期:2016-07-05 12:36:0515-16赛季CBA联赛之广东
日期:2016-06-29 11:45:542015亚冠之全北现代
日期:2015-07-22 08:09:472015年辞旧岁徽章
日期:2015-03-03 16:54:15巨蟹座
日期:2014-12-29 08:22:29射手座
日期:2014-12-05 08:20:39狮子座
日期:2014-11-05 12:33:52寅虎
日期:2014-08-13 09:01:31巳蛇
日期:2014-06-16 16:29:52技术图书徽章
日期:2014-04-15 08:44:01天蝎座
日期:2014-03-11 13:06:45
4 [报告]
发表于 2012-06-16 14:54 |只看该作者
2. 面向c++对象模型角度 看待虚函数
------ 知道这玩意儿有意义吗?C++似乎没有规定必须怎么样实现它

论坛徽章:
0
5 [报告]
发表于 2012-06-16 15:12 |只看该作者
纯虚函数(对应虚基类)

这个是错的。有纯虚函数的类是抽象类。虚基类是继承时列表中virtual修饰的基类。

dtor为什么要设置为虚函数(只用调用子类dtor一次,就销毁了所有资源)

不是“只要”,是防止误用导致难以发现的错误。

关于最后一个问题:如果只需要编译时确定可使用CRTP静态多态。其它情况下,能写出比编译器更优化的代码机会不多。

论坛徽章:
0
6 [报告]
发表于 2012-06-16 15:40 |只看该作者
本帖最后由 titer1 于 2012-06-16 15:41 编辑
bruceteen 发表于 2012-06-16 14:54
2. 面向c++对象模型角度 看待虚函数
------ 知道这玩意儿有意义吗?C++似乎没有规定必须怎么样实现它


其实,这里说的面向c++模型,
详细点,就是知道c++ 累的内存布局 ,
如果不知道类的内存布局,虚函数只是入门,我认为。

第二,虽然c++标准没有规定实践方式,
但是
具体常用的平台不过windows、linux,对应2种编译器cl、gcc。看看gcc,cl下如何实现c++类,实现虚函数,我想这是很有意义的。


论坛徽章:
14
巨蟹座
日期:2013-11-19 14:09:4615-16赛季CBA联赛之青岛
日期:2016-07-05 12:36:0515-16赛季CBA联赛之广东
日期:2016-06-29 11:45:542015亚冠之全北现代
日期:2015-07-22 08:09:472015年辞旧岁徽章
日期:2015-03-03 16:54:15巨蟹座
日期:2014-12-29 08:22:29射手座
日期:2014-12-05 08:20:39狮子座
日期:2014-11-05 12:33:52寅虎
日期:2014-08-13 09:01:31巳蛇
日期:2014-06-16 16:29:52技术图书徽章
日期:2014-04-15 08:44:01天蝎座
日期:2014-03-11 13:06:45
7 [报告]
发表于 2012-06-16 15:44 |只看该作者
回复 6# titer1
我和你看法相反,我觉得研究这玩意儿还不如去研究 ++i + ++i 为什么在某个编译器上等于某个值

论坛徽章:
0
8 [报告]
发表于 2012-06-16 15:44 |只看该作者
本帖最后由 titer1 于 2012-06-16 15:51 编辑
folklore 发表于 2012-06-16 14:25
回复 2# titer1


谢谢你的提醒,我理解错了,不是函数地址没确定,是 子类成员还没有初始化。

论坛徽章:
0
9 [报告]
发表于 2012-06-16 15:50 |只看该作者
回复 7# bruceteen


    呵呵,其实在编译器这层很多文章可以做,
你所说的也是一个不错的技术点。

我想在这里表达的,要做一个合格的c++程序员,必须要懂一个类的 内存布局。

论坛徽章:
0
10 [报告]
发表于 2012-06-16 15:55 |只看该作者
本帖最后由 titer1 于 2012-06-16 16:06 编辑

初次在这里发帖,很多要学啊。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP