深度探索C++对象模型 1

第一章:关于对象

 

加上封装后的布局:

如果C++class和C struct含有相同数据成员,那么C++class并没有增加成本。因为,C++ class中的成员函数不出现在object中(non-inline 成员函数只会诞生一个实例,而inline函数则会在调用该C++ Class的文件模块中产生一个实例),所以C++支持的封装性质并未带来任何空间或执行期的不良后果。

 

C++在布局以及存取时间上主要的额外负担是由virtual引起的(包括虚函数机制和虚基类),此外多重继承下也会有额外负担(发生在一个子类和它的第二个或后继基类之间的转换时)。

 

1.1C++对象模型(书中写的是模式)

C++包含两种数据成员(static和nonstatic)和三种成员函数(static,nonstatic和virtual)。

 

C++对象模型:

Bjarne Stroustrup博士(C++之父,C++语言的创始人,著有《The C++ Programming Language》,《The Design and Evolution of C++》)将Nonstatic data members放在每一个classobject内,static data members放在class object之外,static和nonstatic function members也放在class object之外。virtualfunctions则以两个步骤支持:1.每个class产生出一堆指向virtual functions的指针,放在表格之中,这个表格成为 virtual table(简称vtbl)。2.每个class object被安插一个指针(称为vptr),指向相关的virtual table。vptr的设定和重置都由每一个class的constructor、destructor和copy assignment运算符自动完成(第五章讨论)。每一个class所关联的type_info object(用以支持runtime type identification, RTTI)也经常由virtual table被指出来,通常放在表格的第一个slot。

例如有以下一个类Point:

class Point{

public:

         Point(floatxval);

         virtual~Point();

        

         floatx() const;

         staticint PointCount();

private:

         virtualostream&

                   print(ostream &os ) const;

        

float _x;

static int_point_count;

} ;

则下图表示C++ 对象模型应用在Point类上。


这个模型的主要优点在于空间和存取时间的效率;缺点是如果应用程序代码本省未曾该表,但所用到的class object的nonstatic data member有所改变,那么那些应用层序也得重新编译。

 

加上继承:

C++最初采用的继承模型并不运用任何间接性:base class subobject的data members被直接放置于derived classobject中。优点:提供base class members最紧凑而且最有效率的存取;缺点:baseclass members的任何改变,任何用到该base class或该base class的derivedclass object都需要重新编译。

 

C++2.0之后导入virtualbase class,需要一些间接的base class表现方法:virtual base class的原始模型在classobject中为每一个有关联的virtual base class加上一个指针,或者在classobject中导入virtual base class table,又或者扩充原已存在的virtual table,以便维护每一个virtualbase class的位置。

 

对象模型如何影响程序:

不同的对象模型会导致“现有的程序代码必须修改”以及“必须加入新的程序代码”两个结果。

例如下面这个函数,其中class X定义了一个copy contructor、一个virtualdestructor和一个virtual function foo:

X foobar(){

         X xx;

         X *px = new X;

        

         //foo()是一个虚函数

         xx.foo();

         px->foo();

        

         delete px;

         return xx;

};

这个函数有可能在内部转化为:

Void foobar( X &_result ){  //此处&为引用

 

         //构造_result

_result.X::X();

        

//扩展X *px = new X;

px = _new( sizeof( X ) );

if( px != 0 )

         px->X::X();

 

//扩展xx.foo() 但不使用virtual机制

foo(& _result);                   //此处&为取地址符

 

//使用virtual扩展px->foo()

( *px->vtbl[ 2 ] )( px  );

 

//扩展delete px;

( *px->vtbl[ 1 ] )( px );

_delete( px );

 

return;

}

下图可解决 *px->vtbl[ 1 ] 和 *px->vtbl[2 ] 的含义。

 

1.2  关键词所带来的差异

 

1.3对象的差异:

C++程序支持三种模型

1. 程序模型:像C程序一样

2. 抽象数据类型模型(也被称为object-based OB):有封装,但没有多态和继承。比如string

3. 面向对象模型(object- oriented OO):有封装,多态,继承

 

C++以下列方式支持多态:

1.由一组隐式的转化操作:父类指针指向子类对象,或父类引用类型引用子类对象

2.经由虚函数机制

3.经由dynamic_cast和typeid运算符

 

一个class object需要的内存一般而言由三部分组成:

1.      其nonstatic data members的总和大小(包括父类的nonstaticdata members)

2.      加上任何由于alignment而需要的填充空间。(alignment就是将数值调整到某数的倍数。在32位计算机上,alignment为4bytes,以使运算量达到最高效率)

3.      加上为了支持virtual而由内部产生的任何额外负担

 

指针的类型:

“指针类型”会教导编译器如何解释某个特定地址中的内存内容及其大小。

如:有一个对象有两个数据成员,类型分别为int和string,还有一个指向该对象的指针,则该指针将涵盖12bytes内存空间。(因为int占四个,string占8个包括一个字符指针和一个表示长度的整数);如果该对象有虚函数,则指向该对象的指针还要加4bytes,因为该对象会含有一个vptr指针。

我们不知道void*指针将涵盖怎样的地址空间,这就是void*只能够支持一个地址,而不能够通过它操作所指object的原因。

 

加上多态之后:

用一个父类指针指向一个子类对象,则这个指针所涵盖的地址只包含子类对象中的父类部分。可以用dynamic_cast和static_cast将父类指针转换为子类指针。

一个指针或引用之所以支持多态,是因为它们并不引发内存中任何“与类型有关的内存委托操作”;会受到改变的,只有它们所指向对象的内存的“大小和内容的解释方式”。

然而,如果将子类对象赋值给父类对象,子类对象的子类部分会被切割,父类部分保留。于是多态不在呈现。而一个严格的编译器可以在编译时期解析虚函数,但编译时期的虚函数为静态虚函数,也就是父类的虚函数,所以回避了virtual机制(在运行期解析的虚函数为指针所指对象的虚函数)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值