前言
对于传统结构化语言,如C语言,虽然编译器在背后也做了一下事情,但是很好理解。如编译器堆栈建立、参数排列、返回地址、堆栈清楚等。而对于面向对象的语言,编译器背着我们做了太多,如构造函数、析构函数、虚函数、继承、多态、合成额外函数、扩张我们函数的内容等。
不同的对象模型会有不同的执行效率,因为编译器会根据对象模型对程序做不同的改写。
C++对象模型
有如下三种:
模型一重要!
一个class对象需要多少内存呢,一般是一下几点的和:
- nonstatic数据成员的大小之和;
- 加上由于对齐而产生的填补(与机器有关);
- 加上为了支持virtual而增加的额外负担(与编译器有关 )。
另外值得注意的是一个没有nonstatic的类的对象的大小是1,这是被编译器安插的一个char类型,为了让该类的每个对象具有独一无二的内存地址。
模型二
模型三
C++的继承
- 单继承:如图 class ostream: public ios{...};
- 多继承:如左图 class iostream: public ostream, public istream{...};此处的需注意,若以图中的继承关系,我们会发现iosteam对象中有两份ios的对象部分,为了解决这个问题就需要virtual base class,也就是虚继承。
- 虚继承:如右图 class ostream: virtual public ios{...}; class istream: virtual public ios{...}; 这样无论virtual base class 在继承链中出现多少次,都只会出现一个实体。
那么加上继承后,derived class object的对象模型是什么样的呢,该如何表现出base class subobject呢?有如下几种模型:
模型一重要!
- 对于base class object的nonstatic 数据成员,一般直接放置在derived class object中。
- 在virtual bass class的原始模型是在class object中为每一个有关联的virtual Base class 加上一个指针。引入对于virtual base class后,对象模型为了维护每一个virtual base class的位置,不是导入一个virtual base class table,就是扩充已存在的virtual table。
如
模型二
基于简单模型,增加一个slot,里面存放父类的实体的地址。
模型三
每一个子类对象内含一个地址bptr(类似于vptr),指向一个base class table(其中的每个slot对应一个base calss subobject)。
C++的多态
在C++中,多态只存在于public的继承体系中。C++用下列方法来支持多态:
- 经由一组隐含的转换操作,如把一个derived class指针转化为一个指向其public base 类型的指针。
- virtual function机制;
- dynamic_cast和typeid运行算符。
另外指针是C++多态的必要条件
空类型
1、一个没有任何数据成员和成员函数的类型的sizeof是1,因为如果我们要使用一个类型的实例,该实例必须在内存中占用一定的空间,否则无法使用。,当然大小也可以比1大,具体由编译器决定。
2、当在1的基础上,加上非虚成员函数后,sizeof还是不变。
3、当在1的基础上,加入虚函数后,由于此时需要以指向虚函数表的虚指针,所以sizeof为指针的大小,32位机为4字节,64位机为8字节。
其他知识点
C语言中,数据和函数(对数据的操作)是分开的。
C++同时支持OB和OO两种设计。
OB :Object Based ,非多态的数据封装模型,就是ADT(Abstract Data Type)的概念,提供了类型对象的一些接口和操作。
OO :Object Oriented ,支持多态,需要virtual的支持,这导致了内存布局和存取时间上的额外负担。virtual机制包括virtual function机制(用以支持执行期动态绑定),virtual base class (用以实现“对此出现在继承体系中的base class,有一个单一而被共享的实体”)。
C++中struct和class的用法基本一样,只是继承和成员的默认权限不同,class默认是private继承和private成员,而struct默认是public继承和public成员。
指针:指针的值是所指对象的首地址,偏移量有指针类型决定。base class的指针可以指向继承体系中的任一类型的对象。
参考
《深度探索C++对象模型》