对象模型基础
C++在C的基础上引入了面向对象的程序设计思想,类的封装、继承和多态使得C++功能更加强大,C++的类包含数据成员和成员函数: 数据成员: 静态数据成员,非静态数据成员 成员函数: 非静态成员函数、静态函数、虚函数 C++对象模型研究C++类的数据成员和成员函数在内存中如何布局的问题: 例如:
class Base
{
public:
int member;
static int smember;
void func();
static void sfunc();
virtual void vfunc();
}
在简单对象模型中,类的每个实例化对象在内存中都会申请一块连续内存,在这块内存上保存着指向成员变量和函数的指针 如图所示: 优点: 每个对象的内存大小是固定的,不管是数据成员还是成员函数,都只对应一个指针,在访问时可以很快计算出指针偏移量 弊端: 当访问对象的数据成员时,需要先找到数据成员的指针,然后通过这个指针获取到真实的数据,多了一次寻址,效率较低
-
表驱动对象模型 表格驱动对象模型对类中的数据成员和成员函数进行了区分,每个对象保留两个指针,一个指向数据成员表,其中存放数据成员的取值,一个指向成员函数表,其中存放指向成员函数的指针 如图: 优点: 对数据和函数进行分开管理 弊端: 访问成员函数时,需要先找到成员函数表,效率更低 -
C++对象模型 以Base类为例,C++对象模型如图所示: 可以看出: -
C++对象模型中有一个指向虚函数表的指针,这个虚函数表中除了存放虚函数的指针之外,还有一个type_info的信息,用于在运行时识别对象的正确类型。 -
C++对象的非静态成员变量存放在对象的内存空间中,静态成员变量存放在全局数据区 -
C++对象的成员函数存放在代码区
普通继承场景下的对象模型
- 单一继承场景
以Base为基类,定义派生类Derived如下:
class Derived : public Base
{
public:
int member;
static int smember_d;
void func();
static void sfunc_d();
virtual void vfunc();
virtual void vfunc_d();
}
对象模型如下: 派生类有自己的虚函数表,从基类那边拷贝一份,然后进行修改:将type_info改为派生类的信息;如果派生类重写了基类的虚函数vfunc,那么虚函数表中用Derived::vfunc()的指针替换Base::vfunc()的指针;如果派生类新增了虚函数vfunc_d,那么虚函数表中新增一个函数指针指向Derived::vfunc_d()。
- 多重继承场景
单一继承场景下,派生类在基类的虚函数表上进行修改。多重继承场景,有多个基类的虚函数表。定义基类Base1和派生类Derived如下:
class Base1
{
public:
int member1;
virtual void vfunc();
virtual void vfunc1();
virtual void vfunc2();
};
class Derived : public Base, public Base1
{
public:
int member_d;
static int smember_d;
void func();
static void sfunc_d();
virtual void vfunc();
virtual void vfunc1();
virtual void vfunc_d();
};
此时Derived的对象模型为:
继承多个父类时,派生类的对象模型中分别存储一个虚函数表的指针。如果派生类重写的基类同名虚函数,那就在虚函数表中进行替换;如果派生类新增了虚函数,那么在第一个父类的虚函数表中进行新增扩展。
|