- C语言中,数据与处理数据的操作(函数)是分开声明的,这种程序方法被称为程序性的;而在c++中,则是使用abstract data type(ADT)或class hierarchy的数据封装
- c++对于结构体和函数(不包含virtual和non-inline)的封装并没有增加布局成本,对于C++而言,在布局以及存取时间上主要的额外负担是由virtual引起
- virtual function
- virtual base class
The c++ Object Model
-
c++中,有两种class data members:static and nonstatic,和三种class member functions:static、non-static and virtual
-
现有以下class声明:
class Point { public: Point(float xval); virtual ~Point(); float x() const; static int PointCount(); protected: virtual ostream& print( ostream & os ) const; float _x; static int _point_count; }
-
此 class 可以通过三种object models表示
A Simple Object Model
-
一个object是一系列slots,每一个slots指向一个member,Members按其声明顺序,各被指定一个slot。每一个data member or function member都有自己的一个slot
-
目的:尽量减低C++ complier的设计复杂度,但会损失空间和执行器的效率
- 意义:应用到c++的"指向成员的指针"观念中
A Table-driven Object Model
- 将所有members的信息抽离出来放在一个member data table 和 一个member function table中,而class则含有指向这两个table的pointer
- 意义:成为virtual function的一个方案
The c++ Object Model
- 此模型从A Simple Object Model派生而来,并对内存空间和存取时间优化
- Nonstatic data members被配置于每一个class object内,static data members、Static function members、 nonstatic function members则被存放于个别的class object外。而用virtual functions来支持此model:
- virtual tabel(vtbl)中存放一些指向由class生成的virtual functions的pointer
- 每个class object安插一个指针(vptr),此指针指向相关的virtual table。而vptr的setting 和 resetting都有class里的constructor、destructor、copy assignment运算符自动完成。每个class关联的type_info object(支持runtime type identification,RTTI)亦由virtual table指出,通常放于表格中的第一个slot
- 优点:空间和存取时间效率高
- 缺点:若应用程序本身未改变,而class内的nonstatic data members有所修改,则应用程序代码需重新编译
- c++支持单一继承、多重继承、虚拟继承。虚拟继承中,base class不管在继承串链中被派生多少次,永远只存在一个实例subobject
A Keyword Distinction
- C++为了维护和C之间的兼容性,为此付出了很多牺牲
int (*pq) ();
- 当语言无法区分此函数是declaration还是invocation时,需要一个超越语言范围的规则,此规则会将此函数判断为declaration
- struct的一个合理用途:将class object某个数据做封装,达到与C的兼容的空间布局,只有composition才支持
- template并没有兼容struct
An Object Distinction
- c++程序设计模型支持三种programming paradigms:
- procedural model
- abstract data type model
- object-oriented model
- object-oriented model中,被指定的object的真实类型在每个特定执行点前无法解析,只有通过pointer 和 reference才能完成;相反,abstract data type model中,所处理的是一个固体且单一类型的实例,真实类型早在编译时期就已定义,因此速度更快且空间排布更紧凑,但没有弹性
- c++用三种方法支持多态:
- 经由一组隐式转化。如把derived class pointer转化为public base type的pointer
- 经由virtual function机制
- 经由dynamic_cast和typeid运算符
- 多态主要用途:由一个共同的接口影响类型封装,此接口通常被定义在抽象的base class
- class object所需的内存大小:
- nonstatic data members的总和大小
- 任何由于alignment(将数值调整到某数倍数)的需求而padding的空间
- 支持virtual产生的额外负担
The Type of a Pointer
- "指针类型"告诉编译器如何解释目的地址中的内容和大小,这也是为什么pointer和reference支持多态
- 我们并不知道void*指针涵盖的地址空间,因此void*的指针只含有一个地址,且不能操作所指向的object
- cast本质上是编译器指令,它并不改变指针所含的地址,而只影响"所指内存的大小和其内容"的解释方式
Adding Polymorphism
class A
{
public:
virtual action1();
...
}
class B : public A
{
//以下皆为B独有
public:
...
void rotate();
protected:
enum action2 {...};
int n;
}
B b;
A* p1 = &b;
B* p2 = &b;
-
显然,我们并不不能用p1处理A以外的members,唯一方法是通过virtual
//以下都可行 (static_cast<B*> (p1))->n; if( B*p3 = dynamic_cast<B*>(p1) ) //此为run-time operation,成本较高 p3->n;
p1->action1()
- p1将在编译器决定以下两点:
- 固定的可用接口
- 此接口的access level
B b;
A a1 = b; //造成sliced
a1.action1(); //调用A中的
- 为什么a1中的vptr不会指向b的virtual table?
- 编译器需确保若object含有一个或一个以上的vptrs,这些vptrs内容不会被base class object初始化或改变
- 为什么action1()依旧是调用的A的?
- 多态并不适用于直接存取objects