《深度探索C++对象模型》读书笔记第一章:关于对象


有点尴尬,这个笔记已经记过一遍了。重新来过的原因是我读到第五章左右觉得自己对这本书认识不够深刻,暂时就没有记笔记。今天正式把书通读了一遍了,然后现在读第二遍,这一遍我每章都会写一篇博客总结,只总结重点,精要,所以会简短一些。我会以条款的形式来总结。


  1. 从C到C++,使用 class 封装数据一般并不会增加成本。数据成员就像在 C struct 之中一样,而成员函数不在 class 内部,不管 inline 或是 non-inline 都只会诞生一个函数实体。C++在布局及存取时间上的主要负担由 virtual 引起,即virtual function 机制以及 virtual base class。此外还有多重继承下,发生在一个派生类和其第二或后继的基类之间的转换
  2. C++对象模型:简单说就是非静态数据成员放在每个 class 的内部,静态数据成员、静态或非静态函数放在类的外部;虚函数机制的支持是在每个 class 内部维护一个 vptr 指向 vtbl,vtbl 存放所有虚函数指针。 vptr 的设定和重置都由每一个 class 的构造函数、析构函数和拷贝构造/赋值函数自动完成。每一个 class 的 typeinfo 也放在 vtbl 之中(用来支持 RTTI),vtbl 中还有 virtual base class  subobject 的 offset 值。
  3. C++中尽量不要对 class 使用柔性数组,因为如果该 class 有多个访问权限,或者从另一个 class 派生而来,或者定义有多个 virtual function,可能不能顺利转化。C++中处于同一访问权限的数据必然依声明次序出现在内存布局中,放置在不同访问权限中数据的先后就不一定了;同样的道理,基类和派生类数据成员的布局以及 vptr 的位置也为该伎俩打上一个问号(实际上目前编译器 vptr 一般位于头部,基类数据在派生类数据成员之前,所以后两点视编译器而定吧)。所以要避免使用柔性数组,避免覆盖。
  4. C++中多态仅存在一个个的 public class 体系中。Nonpublic 的派生行为(非公有继承实际上可以通过 reinterpret_cast 强转派生类指针为基类指针实现多态)和 void* 指针可以说是多态,但它们没有被语言明确支持,必须由程序员通过转型操作来管理(这里不谈编译时多态,如函数重载,模板技术等)。
  5. 主要有三种方法支持多态:(1)基类使用引用或指针接收派生类。(2)可调用虚函数触发多态。(3)经由 dynamic_cast 和 tyepid 运算符。
  6. class 的大小由三点决定:(1)其非静态数据成员的大小。(2)内存对齐。(3)为支持 virtual 产生的额外负担(无非是 vptr 和 virtual base class suboject)。
  7. 当一个 基类对象被直接初始化为(或是被指定为)一个派生类对象时,派生类对象就会被切割,仅剩基类类型大小的内存。多态不再呈现,而一个严格的编译器可以在编译时期解析一个 ”通过该对象而触发的虚函数“ 调用操作(编译器够严格,解析出发生了切割,就不采用多态了),因而回避 virtual 机制。如果虚函数被定义为 inline,此时一个非虚函数+ inline 导致效率更高。(不过可惜,可能你想要的多态因为误用失败了)。

总结:
        总而言之,多态是一种威力强大的设计机制,允许通过将派生类的指针赋给基类指针(或基类持有派生类引用),让基类可以 根据当前赋值给它的子类的特性以不同的方式运作。需要付出的代价就是额外的间接性,包括 内存的获得(虚表等)和 类型的决断(RTTI)两方面。
        C++不仅支持 OO(object-oriented,面向对象,也支持 OB(object-based,基于对象)。一个基于对象的设计可能比一个面向对象设计速度更快而且空间更紧凑。速度快是因为所有的函数引发操作都在编译期解析完成,对象建构起来不需要设置 virtual 机制;空间紧凑则是因为每一个类对象不需要 负担传统上为了支持 virtual 机制而需要的额外负担。不过,OB 设计比较没有弹性。


第一章就先到这里了,这只是第一章,这本书的价值,可见一斑。

你可能感兴趣的:(C/C++)