深度探索C++对象模型笔记(一)

好书需要多看,每次都能看到或领悟到新东西。再一次看此书,记录一些自己的理解。

1、加上封装后的布局成本(指的是与C的对比
答案是,在virtual没有参与的情况下,C++的封装没有增加成本。C++在布局以及存取时间上的主要而外负担是由virtual引起的,包括:
  • virtual function机制(runtime binding
  • virtual base class (解决多继承菱形继承问题

此外还有一些多继承下的额外负担,发生在“一个derived class和其第二或后继之base class 的转换“之间。一般言之,并没有什么天生的理由说C++程序一定比其C兄弟庞大或者慢。

---理解了以上,回忆一下多本MFC的书籍都有说过MFC的消息路由,本可以全部通过多态实现,但还是抛弃了虚机制的额外负担,而创造了MFC特有的消息路由方式。这一取舍的缘由了。

2、C++对象模式
  • 简单对象模型:一个object是一系列的slots,每一个slots指向一个members(此模型没有被应用于实际产品
  • 表格驱动对象模型:将所有与members相关的信息抽出来,放在一个data member table和一个member function table中,class object内含指向这两个表格的指针(也未实际应用)
Stroustrup(C++之父)当初设计的C++对象模型是从简单对象模型派生而来的,并对内存空间和存取时间做了优化。此模型中,Nonstatic data members被配置于每一个class object之内,static data members则被存放在所有class object之外,Static 和 nonstatic function members 也被放在所有class object之外,virtual functions则以两个步骤支持之:
  • 每一个class产生出一堆指向virtual functions 的指针,放在表格之中,这个表格被称为virtual table(vtbl
  • 每一个class object被添加一个指针,指向相关vtbl,通常这个指针被称为vptr,vptr的设定(setting)和重置(resetting)都由每一个class的constructor、destructor和copy assignment运算符自动完成。每一个class所关联的type_info object(用于支持runtime type identification, RTTI)也经由virtual table被指出来,通常是放在表格的第一个slot处。
---此处,virtual的加入,每个类多出一个vbtl(具体存放位置C++没有规定,估计与具体平台或编译器实现有关),每个对象至少多了一个vptr,但也正是这个vptr和vtbl的存在而实现了多态

加上继承
C++最初采用的继承模型并不运用任何的间接性:base class subobject的data members被直接放置于derived lcass object中,这提供了最紧凑最有效率的存取,缺点呢?当然就是:base class members的任何改变,都使得所有用到此base class 或其derived class的object者必须重新编译。
自C++2.0新导入的virtual base class,需要一些间接的base class 表现方法。( 后续的章节会有讨论
3、关键词所带来的差异
若不是为了维护与C之间的兼容,C++远比现在更简单。
”什么时候一个人应该使用struct取代class?“答案之一是:当它让一个人感觉比较好的时候。
C++要支持现存的C程序,就不能不支持struct。那么它需要引入class吗?真的需要吗?不!但是引入它的确非常令人满意,因为这个语言所引入的不只是关键词,还有它所支持的封装和继承的哲学。
你可以主张说这个关键词的使用伴随着一个public接口的声明,甚至可以说它的用途只是为了方便C程序员迁徙至C++部落。
---不需要纠结struct和class了,struct实际就是默认public域,为了兼容C程序而存在,当在开发C++程序时,不使用它,也毫无压力。
策略性正确的struct
c程序员的技巧有时候可能会成为C++程序员的陷阱,如把单一元素的数组放在一个struct的尾端,于是每个struct object可以拥有可变大小的数组。
struct mumble
{
  /*stuff*/
  char pc[1];
}
struct mumble *pMumble = (struct mumble*)malloc(sizeof(struct mumble) + strlen(string) + 1);
strcpy(&memble.pc, string);

以上在C是可行的,若改用C++的clas来做,或许可以顺利转化,或许不行。
C++中凡处于同一个access section的数据,必定保证以其声明次序出现在内存布局中,然而被放置在多个access sections中的各笔数据,排列次序就不一定了。因此最好的忠告:不要那么做

如果迫切需要一个相当复杂的C++ class的某部分数据,使它拥有c声明的那种样子,那么那一部分最好抽取出来成为一个独立的struct声明。利用组合,而非继承。(conversion 运算符提供了一个十分便利的萃取方法)

struct C_point{...};
class Point
{
public:
         operator C_point(){return _c_point;}
private:
          C_point   _c_point;
}
C struct在C++中的一个合理用途,是当你要传递”一个复杂的class object 的全部或部分“到某个C函数中去时,struct的声明可以将数据封装起来,并保证拥有与C兼容的空间布局,然而这项保证只在组合的情况下才存在,如果是继承而非组合,编译器会决定是否应该有而外的data members 被安插到 base struct subobject之中。
---MFC封装很多GDI的类都是使用此种方式,如CRect 可直接与RECT转换。

4、对象的差异
虽然你可以直接或者间接处理继承体系中的一个base class object, 但只有通过pointer或reference的间接处理,才支持OO程序设计所需的多态性质。
C++以下列方法支持多态:
  • 经由一组隐含的转换操作,例如把一个derived class指针转化为一个指向其public base type的指针:
    Shape *ps = new Circle();

  • 经由virtual function机制:
    ps->rotate();
  • 经由dynamic_cast和typeid运算符:
    if(circle *pc = dynamic_cast<circle*>(ps))
需要多少内存才能表现一个class object?一般而言要有:
  • nonstatic data members的总和大小
  • 加上任何由于alignment的需求而填补上去的空间(可能存在与members之间,也可能存在与集合体边界)
  • 加上为了支持virtual而由内部产生的任何额外负担
一个指针(或是一个reference,本质上一个reference通常是以一个指针来实现的),不管它指向哪种数据类型,指针本身所需的内存大小是固定的。一个机器地址,即机器字长,如32位机器是4bytes
一个 pointer或一个reference之所以支持多态,是因为它们并不引发内存中任何”与类型有关的内存委托操作(type-dependent commitment)“;会受到改变的只是它们所指向的内存的”大小和内容解释方式“而已。
当一个base class object被直接初始化为(或是被指定为)一个derived class object时,derived object就会被切割,以塞入较小的base type内存中,多态于是不再呈现。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值