后记

  结合前面的讨论,我们可以看到,只要牵涉到了虚继承,在访问父类的成员变量时生成的代码相当的低效,需要通过很多间接的计算来定位成员变量的地址。在指针类型转换,动态转型,及虚函数调用时,也需要生成很多额外的代码来调整this指针。象前一篇中对C170对象的obj.foo()和obj.f170()两次调用,传递到两个函数中的this指针居然是不一样的。
  前面我们碰到过的怪异行为还有很多,比如偏移值指针指向地址的前4字节,及C150、C170对象中的4字节0值的语义,为什么对C150和C170调用foo函数时,this指针指向的不是子类部分的起始位置而是祖父类的起始位置,等等。去彻底的探究这些问题的意义并不是很大。虚继承的实现属于编译器厂商的行为,厂商出于不同的考虑,实现的方法也会大相径庭。
  对于传统的C程序员,他们可能会认为C++的效率低。其实效率低是低在多态部分,因为这要在运行时通过虚表来决议出函数的地址。但对于设计而言,多态是一个非常强大的武器。多态也是面向对象设计的核心技术之一。虽然在执行的效率上有所损失,但对于大规模的程序设计,对于问题域到模型的映射,使用以多态为核心的面向对象设计技术可以提高设计、实现及维护的效率,对于大部分的应用,总体来说得大于失。
  但是对于虚继承,个人感觉只是为了解决菱形继承及更复杂继承问题不得已而引入的一项机制,而且没有完美的解决方案,不但大幅的损失效率,而且带来了巨大的复杂性,使得继承结构晦涩难懂。 如非万不得已,且在自己清楚一切后果的情况下,建议不要使用。尤其是不要在被虚继承的基类中声明非静态的成员变量。
  C++支持多种编程范型,面向过程式的、数据抽象及封装、面向对象、现在又多了一种基于模板技术的泛型编程。我们以一个优秀的开源C++编程环境ACE(之所以叫编程环境,因为它提供了从类库到框架的多层次的支持)为例,看看在设计时的衡量及各种编程范型的运用契机。ACE分几个层次,依次为:OS适配层、wrapper facade层、框架层、服务组件层。OS适配层、wrapper facade层主要运用了数据抽象及封装,没有用到多态及虚机制。因为这两层的关键是效率。而且在这两层中的类的成员方法尽量的内联。其实不使用多态及虚机制的话,C++的效率和C应该是差不多的,但对象封装导致了大量的琐碎方法(如对成员变量的访问封装,即set,get方法),而方法调用的成本也是相当高昂的(需要保存及恢复当前的执行环境上下文,参数的传递及返回值可能产生很多的临时变量及对象等)。所以这两层通过内联来减少函数调用的开销,提高执行效率。在框架层则使用了大量的设计模式,大量使用了多态机制及泛型技术。在这一层的主要关注点是结构的清晰,及实现设计上的语义。在这时多态机制在执行效率上的损失是可以忽略不计的。

  最后,我用Lippman在他的经典书籍《Inside the C++ Object Model》中关于描述虚成员函数章节中的一段话来做为这系列文章的结束。“Although I have a folder full of examples worked out and more than one algorithm for determining the proper offsets and adjustments, the material is simply too esoteric to warrant discussion in this text. My recommendation is not to declare nonstatic data members within a virtual base class. Doing that goes a long way in taming the complexity.”大意为在虚继承时用以确定偏移地址及进行this指针调整的可行算法很多,而且大都非常的诡异(这个我们已经见识了:))。同时他建议不要在被虚继承的基类中声明非静态的成员变量,这样做纯属自取烦恼。

  另,感谢张水松和张建业这两个土人,在写这些文章时和他们进行了一些非常有益的探讨。最后也是他们提醒我,不要再深入下去,以免走火入魔。

  (全文完)

潘凯:C++对象布局及多态实现的探索(十二)相关推荐

  1. 潘凯:C++对象布局及多态实现的探索(十)

    菱形结构的虚继承(2) 我们再看一个例子,这个例子的继承结构和上一篇中是一样的,也是菱形结构.不同的是,每一个类都重写了顶层类声明的虚函数.代码如下: struct C041 {     C041() ...

  2. 潘凯:C++对象布局及多态实现的探索(九)

    菱形结构的虚继承 这次我们看看菱形结构的虚继承.虚继承的引入本就是为了解决复杂结构的继承体系问题.上一篇我们在讨论虚继承时用的是一个简单的继承结构,只是为了打个铺垫. 我们先看看这几个类,这是一个典型 ...

  3. 潘凯:C++对象布局及多态实现的探索(六)

    虚函数调用 我们再看看虚成员函数的调用.类C041中含有虚成员函数,它的定义如下: struct C041 {     C041() : c_(0x01) {}     virtual void fo ...

  4. 潘凯:C++对象布局及多态实现的探索(一)

    前言 本文通过观察对象的内存布局,跟踪函数调用的汇编代码.分析了C++对象内存的布局情况,虚函数的执行方式,以及虚继承,等等. 写这篇文章源于我在论坛上看到的一个贴子.有人问VC使用了哪种方式来实现虚 ...

  5. 潘凯:C++对象布局及多态实现的探索(三)

    带虚函数的类的对象布局(2) 接下来我们看看多重继承.定义两个类,各含一个虚函数,及一个数据成员.再从这两个类派生一个空子类. struct C041 {     C041() : c_(0x01) ...

  6. 潘凯:C++对象布局及多态实现的探索(十一)

    菱形结构的虚继承(3) 最后我们看看,如果在上篇例子的基础上,子类及左.右父类都各自定义了自己的虚函数,这时的情况又会怎样. struct C140 : public virtual C041 {   ...

  7. 潘凯:C++对象布局及多态实现的探索(八)

    普通的虚继承 下面我们来看虚继承.首先看看这C020类,它从C010虚继承: struct C010 {     C010() : c_(0x01) {}     void foo() { c_ = ...

  8. 潘凯:C++对象布局及多态实现的探索(四)

    类型动态转换和类型强制转换 为了验证前面提到过的类型动态转换(即dynamic_cast转换),以及对象类型的强制转换.我们利用前面定义的C041.C042及C082类来进行验证. 运行下列代码: c ...

  9. 潘凯:C++对象布局及多态实现的探索(二)

    带虚函数的类的对象布局(1) 如果类中存在虚函数时,情况会怎样呢?我们知道当一个类中有虚函数时,编译器会为该类产生一个虚函数表,并在它的每一个对象中插入一个指向该虚函数表的指针,通常这个指针是插在对象 ...

最新文章

  1. 百变冰冰!手把手教你实现CVPR2021最新妆容迁移算法
  2. C 语言字符串分割函数 p = strtok(NULL, );
  3. 9 岁自学编程、24 岁身价涨至数百万美元,与微软一较高低的大佬多厉害?
  4. (转)MySQL 服务器内存使用
  5. 3. golang 流程控制
  6. MOOC数学建模与实验---学习笔记---整理汇总表
  7. OLTP 和OLAP
  8. AbstractQueuedSynchronizer理解之三(Semaphore)
  9. C++/mfc错误总结
  10. mac perl dbd mysql_Install DBD::mysql for Perl in XAMPP in Mac , solving errors
  11. un8.21:用html实现增删改查功能(代码篇)。
  12. Gambit 2.Gambit解释器
  13. 集丰照明|LED 的产业链由哪些部分构成?
  14. Pytorch深度学习(二):反馈神经网络(BPNN)
  15. centos中使用goaccess分析nginx日志,goaccess分析多个nginx日志
  16. 恕我直言,Java四大名著并不一定适合你!
  17. 计算机应用技术的研究方向,2019考研计算机应用技术专业解析:研究方向
  18. 实验8 脉冲宽度调制(PWM)模块 北京化工大学 2019090034
  19. 如何来投放广告更赚钱
  20. SD卡SPI读写模式,基于51单片机的讲解

热门文章

  1. 湾区求职分享:三个月刷题拿到 Google offer,欢迎踊跃提问
  2. Windows无法启动这个硬件设备(代码19)怎么办?
  3. 高耐压30V线性LDO芯片
  4. et2016免狗_2016年网络安全威胁以及如何保护您的网站免受威胁
  5. free5GC安装、运行、测试及注意事项
  6. 如何选择漏电保护器规格型号_如何选择漏电保护器型号?漏电保护器的选型原则...
  7. VirtualBox 安装以及 CentOS Linux 系统环境安装教程
  8. Spring Boot葵花宝典:初现江湖
  9. 单片机实验四(电子琴自动演奏)
  10. VMware Workstation+Servers2003