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

C++ class 相对于 C struct,支持了更多特性,如添加了类的方法,构造析构函数,继承等。一般来说 class 和 struct 效率大部分情况下是相同的,引起效率差异的地方主要在两点:

  • virutal function。class需要维护一个虚函数表,并通过虚指针指向它,中间多了一道访问转换。

  • virutal base class。虚基类,主要是为了解决菱形继承关系,防止基类对象在子类对象布局中存在多份,因而引入了指针,这中间也多了一道访问转换。

C++对象模型

非静态成员变量

非静态成员变量存放在每个object上

静态成员变量

静态成员变量存放在静态数据区,需要在类外单独定义。例如

1
2
3
4
5
class A {
public:
static int a; //声明
};
int A::a = 1; //定义
成员函数

成员函数,包括非静态和静态成员函数,存放于代码区,也是独立于每个object之外。

虚函数

所有的虚函数被放在类的虚函数表里,虚函数表由编译器生成;每个object有个指向虚函数表的指针,在运行时由构造函数、析构函数、赋值操作符来设置或者重置。

TypeInfo

类的类型信息,用来在运行时支持类型识别 (RTTI),也存放在虚函数表里,通常放在虚表的第一个slot。

总览

未涉及继承的class对象布局,总体的示意图就类似下面这样

image-20181118203839822

C++多态语义

c++多态从语法上说,需要的形式是基类指针或者引用指向子类对象,如

1
2
3
4
5
6
Derived d;
Base *bp = &d;
bp->DoSomething();
//或者
Base &bi = d;
bi.DoSomething();

如果直接将子类对象直接赋值给基类对象,则子类对象会被截断:

1
2
3
4
Base b;
Derived d;
b = d; //截断
b.DoSomething(); //调用的是基类的方法

因此,如果有一个基类的指针*bp 或者引用 bi,我们必然不能确定该指针或引用指向的是什么,可能是基类的对象,也可能是子类的对象,因此其调用的方法必须在运行时确定,这也是多态的具体表现。

但是也要注意,并不是任意一个指针或者引用就支持指针,比如下面

1
2
3
4
5
6
// no polymorphism
int *pi;
// no language supported polymorphism
void *pvi;
// ok: class x serves as a base class
x *px;

以下是多态语法形式的内存布局:

image-20181118212121033

更复杂的形式:

image-20181118212454317