对象
封装成本
C++所有对象之间共享成员函数
每个非内联函数只产生一个函数实体
每个内联函数将在每个使用内联函数的地方都拥有一份副本
内联函数这个特征很好理解,毕竟内联函数是被复制到使用内联函数的地方,而不是按地址调用 |
C++ 封装后的成本主要是由虚有函数和虚有基类造成的:
虚有函数用于支持运行时绑定
虚有基类用来实现
多次出现在继承体系中的基类拥有单一、共享的实体
此外,子类与非第一基类之间的类型转换也会造成额外成本
对象模型
C++根据对数据成员(静态数据成员、非静态数据成员)和成员函数(静态成员函数、非静态成员函数)的处理方式不同,构成了不同的对象模型:
简单对象模型
简单对象模型(Simple Object Model)中对象中只保存成员指针(数据成员指针和成员函数指针),此种情况下 对象的大小 = 指针的大小 x 指针的数量
。
该对象模型降低了编译器的设计复杂度,代价是空间和执行期的效率。
这个模型并没有被应用于实际,但是为 指向成员的指针
提供了概念模型。
表格对象模型
表格对象模型(Table-driven Object Model)中,将数据成员和成员函数各自储存在一张表格中,对象则持有这两张表格的指针。数据成员表格中保存着数据成员的实体,而成员函数指针则保存着成员函数的指针。
这个模型没有被应用于实际,但是为 虚函数
提供了概念模型
C++对象模型
C对象模型(C Object Model)描述如下:
非静态数据成员储存到对象中
静态数据成员储存到对象之外
静态成员函数和非静态成员函数储存于对象之外
虚函数表的指针储存于对象之中
虚函数的支持方法如下:
建立虚函数表(
vtbl (virtual table)
)储存指向虚函数的指针在对象中建立指向vtbl的指针(vptr)。vptr的初始化和销毁由构造函数、析构函数、复制构造函数自动完成。
对象关联的
类型信息对象 (type_info object)
被储存到vtbl的首部(通常情况下)
该模型的优点是空间和效率比较好,但是一旦类的非静态数据成员发生改变,整个软件代码必须要重新编译
C++继承模型
简单对象模型的继承模型
简单对象模型可以通过添加父类指针来储存基类,优点是子类大小与父类无关,但是带来了空间和效率的开销。
基于表格的继承模型
通过构造
基类表 (base table)
可以实现另一种继承模型,其特点为:基类表中包含指向基类的指针(多继承时包含多个基类指针)
每个类对象都包含一个
指向基类表的指针(bptr)
其优点为无需更改类对象就可以更改基类,缺点是带来了储存空间和效率上的负担。
C++编程范型
C++支持三种编程范型:
-
- 过程式编程
例如: +
const char* boy = "Danny"; char* p_son; ... p_son = new char[ strlen(boy) + 1 ]; strcpy(p_son, boy); ... if(!strcmp(p_son, boy) take_to_disneyland(boy);
-
- 抽象数据模型(ADT)
抽象数据模型为用户提供借口,及内部计算过程隐而不提: +
string girl = "Anna" string daugher; ... daugher = girl; // operator=() ... if(girl == daugher) // operator==() take_to_disneyland(girl);
-
- 面向对象模型(Object-Oriented model)
面向对象模型通过抽象基类向外部提供接口,子类对接口进行实现。 +
class Human{ public: virtual void eat() = 0; }; class Chinese: public Human{ public: void eat() override { cout<<"Chinease eat"<<endl; } }; class American: public Human{ public: void eat() override { cout<<"American eat"<<endl; } };
+
Important OO模型的多态依赖于指针(或引用)的间接处理,若使用非指针类型进行类型转换,很可能会丢失信息。因此,使用OO范型的话尽可能使用指针(或引用)。 |
尽量使用一种范型,而不是混用范型 |