一. C/C++ 语言中的方法和数据
1. C语言的数据和方法 语言中数据和处理(函数)是分开的,语言本身不支持数据和函数的关联性。这种方法我们称之为:程序性的;它是由"分布在各个以功能为导向的函数中"d的算法所驱动,它们处理的是共同的数据。
2. C++语言数据和方法
C++中是通过ADT(Abstract Data Type, ADT)来实现的。 C++可以在不同层次上进行抽象,造成的复杂度可能也不一样。
书中从简单到复杂四个层次的抽象: 简单类、继承、一个参数的Template、两个参数的模板。
二.C++加上封装后的布局成本(Layout Costs for Adding Encapsulation)
1. C++中的对象的布局
a. data member: 直接的包涵在每一个class object(注意: 类对象,不是类)之中,这和C struct的情况是一样的
b. member function: 它不出现在class object 之中.
non-inline member: 它会产生一个行数的实体. 如果是非static的funciton, 每个function会加上一个this指针作为function的第一个参数.
inline member: 会在每一个使用者身上产生一个函数的实体。这一般是为了提高效率。
2. C++布局和存取上的额外开销
a. virtual function 机制: 用以支持一个有效的"执行期绑定(runtime binding)"
b. virtual base class
三. C++对象模型(The C++ Object Model)
1. 简单对象模型(A Simple Object Model)
这种模型中,每个object是一系列的slots, 每个slot指向一个member. 每个member按其申明的次序各占用一个slot. 这里的member包括data member 和 function member. 每个member是通过slot的索引来访问的。
具体的模型参看:
2. 表格驱动模型(A Table-driven Object Model)
这种模型中把class object的members分组放在一个data member table 和一个function member table中,class object内含两个指向table的指针. member function table 是一系列的slots, 每个slot指向一个function member. data member table 则是直接的包涵有data本身。
具体的模型参看:
3. C++对象模型(The C++ Object Model)
C++的对象模型如下:
a. nostatic data members 被直接的配置在每一个class object之内。
b. static data member 、static 和 nonstatic function members全部被放在所有的class object 之外。
c. virtual functions 则是以下列步骤支持的:
i. 每一个class 产生一堆指向virtual functions的指针,放在表格之中,我们称这个表格为:virtual table(vtbl).
ii. 每个得class object 被添加了一个指针,指向相关的virtual table,我们把class object的这个指针称之为vptr(virtual pointer);这个vptr的设定和重置是由类的constructor、destructor 和 copy assignment 运算符自动完成的;每个类的type_info object也是经由virtual table指出的,通常是放在表格的第一个slot处。
具体的模型参看:
d. 加上继承(Adding Inheritance)
在 A Simple Object Model 中,每一个基类可以被derived class object的一个slot指出,该slot内含base class subobject的地址。
在虚拟继承的情况下,base class 不管在继承链中被派生多少次,永远只有一个实体(subobject). 书中以iostream继承体系说明。
C++中的base class subobject的data members直接放置于derived class object中。那么它的function members是怎么处理的呢?(我没有理解这块)
对于virtual base class, C++ 2.0 是在class object中添加一个关联 virtual base class object的指针。
e. 对象模型对程序的影响
我觉得书上的这段代码非常好的体现了不同模型对程序的影响
预定义 class X 如下:
class
X
{
public
:
virtual
~
X()
{ }
X
&
X(
const
X
&
rhs)
{ }
virtual
void
foo()
{ }
}
//
定义一个方法
X foobar()
{
X xx;
X
*
px
=
new
X();
//
xx.foo();
px
->
foo();
//
delete px;
return
xx;
}
//
这个函数可能的转化为:
void
foobar(X
&
_result)
{
_result.X::X();
//
px
=
new
(
sizeof
(X) );
if
(px
!=
0
)
px
->
X::X();
//
这里是不使用virtual 机制的foo调用
//
注意这里的调用方法,不是用vtbl,
//
这样如果有从class X 继承的类初始化或赋值给X基类时,
//
调用foo的方法是X的方法, 是编译时确定的
foo(
&
_result);
//
是用virtual 机制的foo调用, 它是运行时确定的
(
*
px
->
vtbl[
2
])(px);
//
delete px
if
(px
!=
0
)
{
(
*
px
->
vtbl[
1
])(px);
//
destructor
_delete(px);
}
//
return
;
四. 关键词所带来的差异(A Keyword Distinction)
讨论了class 和 struct 的差异和选择
五. 对象的差异( A Object Distinction)
1. C++程序设计模型支持三种programming paradigms.
a. 程序模型(procedural model) 就是像 C 一样进行编程
b. 抽象数据类型模型(abstract data type model, ADT) 用对象进行编程
c. 面向对象模型(object-oriented model)
模型中有一些彼此相关的类型,通过一个抽象的base class被封装起来(也就是:接口)。类型之间的操作是通过接口进行的。
纯粹的以一种paradigm写程序是好的.(哈哈,好像这不太可能,我还做不到)
二. 面向对象模型(object-oriented model)
a . C++中多态支持性的支持是通过: pointer 和 reference来实现的.
多态通过下面三种方法来支持:
i. 经由一组隐含的转化操作: shape *ps = new circle();
ii. 经由virtual function 机制 ps->rotate();
iii. 经由 dynamic_cast和typeid来支持:
if(circle *pc = dynamic_cast<circle*>(ps)) ...
多态内存需求
i. 其 nonstatic data members 的总和大小
ii. 任何字节对齐的额外填充(padding)
iii. 支持virtual 而产生的额外负担
b. 指针的类型
"指向不同类型的各指针"的差异,不在于其指针的表示法不同,也不在于其内容的不同, 而是其寻址出来的object的类型不同。也就是说"指针类型"会教导编译器如何解释某个特定地址中的内存内容及其大小.
c. 加上多态之后(Adding Polymorphism)
以如下为例:
1
class
Bear :
public
ZooAnimal
2
{
3
public
:
4
Bear();
5
~
Bear();
6
7
//
8
void
rotate();
9
virtual
void
dance();
10
11
//
12
protected
:
13
enum
Dances
{ }
;
14
15
Dances dances_known;
16
int
cell_block;
17
}
;
18
19
/**/
///
20
Bear b(
"
Yogi
"
);
21
Bear
*
pb
=
&
b;
22
Bear
&
rb
=
*
pb;
23
具体的内存布局如
//
现有
1 Bear b;
2 ZooAnimal *pz = &b;
3 Bear *pb = &b;
4
以上每个都指向Bear object的第一个byte,其间的差别是,pb所涵盖的地址包含整个的Bear object, 而pz所涵盖的地址只包含Bear object中的 ZooAnimal subobject部分。你只能用pz来处理Bear中的virtual functions, 而不能直接的处理Bear中的其他任何members.
注意pz的类型将在编译时确定以下两点:
i. pz固定的可用接口
ii. pz的接口的access level;因为子类的access level可能是不同于基类的,编译时会检测是否可以转换。
e. 对象赋值问题
Bear b;
ZooAnimal za
=
b;
//
ZooAnimal::rotate() invoked
za.rotate();
这里有两个问题
i. za为什么调用的是ZoomAnimal::rotate的实体而不是 Bear的实体?
答:za并不是一个Bear, 它只是一个ZoomAnimal, 多态的这种特性不能用在直接存取的objects上。所以 za.rotate()调用只能是 ZooAnimal::rotate()
ii. 如果初始化函数将一个object的内容完全拷贝到另一个object中去,为什么za的vpt不是指向Bear的virtual table呢?
答:编译器在初始化或赋值操作时,如果某个object含有一个或多个vptrs, 那么这些vptrs的内容不会被原对象初始化或改变.
例如上例的 ZooAnimal za = b, 这里的vptr并不会被 b 的vptr所替代.