Inside The C++ Object Model

1.关于对象

关于封装后的布局成本:

就像struct一样data member直接包含在class object之中,而member function虽然在class的声明之内,却不会出现在object中。每一个noninline member function只会诞生一个函数实体。至于inline function则会在每一个使用者身上产生一个函数实体。

nonstatic member data + alignment + C++ 在布局和存取时间上主要的额外负担是由virtual引起的,包括:

1.virtual function机制用以支持一个有效率的“执行期绑定”

2.virtual base class用以实现“多次出现在继承体系中的base class,有一个单一而被共享的实体”

多态由指向派生类的基类指针或引用实现。也就是说:“指针类型”会教导编译器如何解释某个特定地址中的内存内容和大小。多态的主要用途是经由一个共享的接口来影响类型的封装,共享接口以虚函数机制引发,在执行期根据对象的真正类型解析出到底是哪一个函数实例被调用。

• 静态多态性通常称为编译时多态,到底模板是不是多态???我个人认为不是

• 动态多态性通常称为运行时多态,通过虚函数来实现

C++ADT程序风格,如今也被称为object base一种非多态的数据类型。

在C++对象中表现为中有三种模型(C++对象模型是在前两种对象模型上发展而来的,甚至于局部上直接用到前两种对象模型):

  • 简单对象模型(指向成员的指针)、
  • 表格驱动对象模型(以此模型作为支持虚函数的方案)、
  • C++对象模型。

三种编程典范 - 程序模型:数据和函数分开。 - 抽象数据类型模型(OB):数据和函数一起封装以来提供。 - 面向对象模型(OO):可通过一个抽象的base class封装起来,用以提供共同接口,需要付出的就是额外的间接性。

转型(cast)其实是一种编译器指令。

  • 大部分情况下它并不改变一个指针所含的真正地址,它只影响“被指出之内存大小和其内容”的解释方式
  • 如一个类型为void *的指针只能够持有一个地址,但不能 通过它操作所指object。

2.构造函数语意学

bitwise copy只存在内置数据类型(class object 以同一个class type object 初始化时,bitwise copy就绰绰有余了)。

memberWise copy情况下,编译器必须合成一个copy constructor以便调用member class object的copy constructor。

什么情况下,[一个类自动生成  [ 默认构造函数,拷贝构造函数 ],一个class 对于默认的copy assignment operator,不会出现bitwise copy ] ?就是这个类是一个复杂类(支持继承、多态、含有成员对象的任意情况或组合)

1.带有 [ default constructor, default copy constructor, copy assignment operator ] 的member class object / Base class。

2.带有virtual function / virtual base class的class。

带virtual function的base class object 以driver class object作为初始值,vptr指向的是base class 的virtual table。

每一个编译器对虚拟继承的支持承诺,都表示必须让“driver class object 中的 virtual base class subobject 位置”在执行期间准备妥当。

NVRO:编译器多多少少对你的程序代码做了部分转化。尤其是当一个函数以传值的方式传回一个class object ,编译器将copy constructor 的调用操作优化,以一个额外的第一参数(数值被直接存放于其中)取代NVR。

NVR:以一个额外的第一个参数copy constructor  local object。

编译器会对initialization list 一一处理并可能重新排序,以反映出member 的声明次序。它会安插一些代码到constructor 体内,并置于任何explicit user code 之前。

对于初始化队列,厘清一个概念是非常重要的:(大概可以如下定义)

  • 把初始化队列直接看做是对成员的定义,
  • 构造函数体中进行的则是赋值操作。

关于member initialization list,我们首先需要知道需要它的时机:

1.当初始化一个reference member时;

2.当初始化一个const member时;

3.当调用一个base class的constructor,而它拥有一组参数时;

4.当调用一个member class的constructor,而它拥有一组参数时。

接下来需要注意的一点就是:list中的初始化顺序是由class中member的声明顺序决定的,而不是由initialization list中的排列顺序决定的!这是一个非常容易出错的地方。

3.Data语意学

自然多态:关于 classes 体系中base type 和derived type之间的装换,把一个derived class object指定给一个base class的指针或者引用。该操作并不需要编译器去调停编译器或修改地址。(base class 和derived class的object都从相同的地址开始)

C++的对象模型中,在一个继承而来的类的内存分布里,各个基类需要分别遵循alignment,从而导致了空间的浪费。

多重继承:需要在 &object 上偏移在base class 前面继承声明的类才能取到对应的base class 的内存。

Vptr与Vbptr

  • 在多继承情况下,即使是多虚拟继承,继承而得的类只需维护一个Vbptr
  • 而多继承情况下Vptr则可能要维护多个Vptr,看其基类有几个虚函数。
  • 一条继承线路只有一个Vptr,但可能有多个Vbptr,视有几次虚拟继承而定。换句话说:
    • 对于继承类对象来说,不需要新合成vptr,而是使用其基类子对象的vptr。
    • 而虚拟继承类对象,必须新合成一个自己的Vbptr。

其中差别在于vptr通过一个虚函数表可以确切地知道要调用的函数,而Vbptr通过虚基类表只能够知道其虚基类子对象的偏移量。这两条规则是由虚函数与虚拟继承的实现方式,以及受它们的存取方式和复制控制的要求决定的。

类中对静态成员变量取址: 对静态成员取地址,将会得到一个指向数据类型的指针,而不是一个指向class member的指针,因为静态成员并不内含与class object中.

在VC中数据成员的布局顺序为:

  • vptr部分(如果基类有,则继承基类的)
  • vbptr (如果需要)
  • 基类成员(按声明顺序)
  • 自身数据成员
  • 虚基类数据成员(按声明顺序)

4.Function的语意学

多继承下的虚函数,影响到虚函数的调用的实际质上为this的调整。而this调整一般为两种:

  • 调整指针指向对应的sub object,一般发生在继承类类型指针向基类类型指针赋值的情况下。
  • 将指向sub object的指针调整回继承类对象的起始点,一般发生在基类指针对继承类虚函数进行调用的时候。

“指向Nonstatic Member Functions”的指针

  • 取一个nonstatic data member的地址,得到的结果是该member在 class 布局中的byte位置(再加1),它是一个不完整的值,须要被绑定于某个 class object的地址上,才可以被存取.
  • 取一个nonstatic member function的地址,假设该函数是nonvirtual,则得到的结果是它在内存中真正的地址.然而这个值也是不全然的,它也须要被绑定与某个 class object的地址上,才可以通过它调用该函数,全部的nonstatic member functions都须要对象的地址(以參数 this 指出).

“指向Virtual Member Functions”的指针

对一个 virtual member function取其地址,所能获得的仅仅是一个索引值。通过pmf来调用z(),会被内部转化为一个编译时期的式子,一般形式例如以下: (*ptr->vptr[(int)pmf])(ptr);

5.构造、解构、拷贝的语意学

不要在任何virtual base class中声明数据(subobject 的多重拷贝)

如果class没有定义destructor,那么只有在class自带的member object(或base class)拥有destructor的情况下,编译器才会自动合成一个destructor。

“most-derived class”  虚基类的构造由最外层类控制。

在虚拟继承情况下,copy assignment opertator会遇到一个不可避免的问题,virtual base class sub object的复制行为会发生多次,与前面说到的在虚拟继承情况下虚基类被构造多次是一个意思,不同的是在这里不能抑制非most-derived class 对virtual base class 的赋值行为。安全的做法是把虚基类的赋值放在最后,避免被覆盖。

对象析构语意学

  • 只有在基类拥有析构函数,或者object member拥有析构函数的时候,编译器才为类合成析构函数,否则都被视为不需要。
  • 析构的顺序正好与构造相反:
    • 本身的析构函数被执行。
    • 以声明的相反顺序调用member object 的析构函数,如果有的话。
    • 重设vptr 指向适当的基类的虚函数表,如果有的话。
    • 以声明相反的顺序调用上一层的析构函数,如果有的话。
    • 如果当前类是 most-derived class,那么以构造的相反顺序调用虚基类的析构函数。

6.执行期语意学

T c = a + b 比 c = a + b有效率(一连串的 destructor 和 copy constructor 被 assigned取代)

临时性对象的被摧毁,应该是对完整表达式求值过程中的最后一个步骤。该完整表达式造成临时对象的产生。

例外:1.凡含有表达式执行结果的临时性对象,应该存留到object 的初始化操作完成为止。2.如果一个临时性对象被绑定于一个reference ,对象将残留,直到被初始化之reference 的生命结束,或直到临时对象的生命范畴结束---视哪种情况先到达而定。

7.站在对象模型的类端

1.template

具现行为:不使用不具现

错误报告:编译器只进行语法parse,不进行类型判断。

template中的名称决议方式:

1.1 scope of the template declaration :函数的调用与template参数毫无关联。

1.2 scope of the template instantiation:上反

编译器决议算法必须决定哪一个才是合适的scope, 然后再选择适当的name。

2.exception handlering

只需要处理好user code 带来的 exception。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值