c++对象模型
Object 对象模型
构造函数与析构函数的顺序:
继承关系:构造由上向下,析构由下向上
组合关系:构造由内向外,析构由外向内
关于vptr和vtbl
虚指针和虚函数表
class A {
public:
virtual void vfunc1() {};
virtual void vfunc2() {};
void func1() {};
void func2() {};
private:
int m_data1, m_date2;
};
class B : public A {
public:
virtual void vfunc1() {}
void func2() {}
private:
int m_data3;
};
class C : public B {
public:
virtual void vfunc1() {}
void func2() {}
private:
int m_data1, m_data4;
};
静态绑定:call 0X123456 在编译的代码中写入固定的地址,如C语言中调用函数
动态绑定:call vtbl[*] 在编译的代码中用虚函数表中的项作为函数的调用地址
通过对象调用函数一定是静态绑定
调用函数满足三个条件则是动态绑定:
- 通过指针调用
- 调用虚函数
- 存在向上转型
C++ 通过虚成员函数表 vtable 实现多态,虚函数表中存储的是类中虚函数的入口地址。普通类中是没有虚函数表的,只有在具有虚函数的类中(无论是自身添加的虚函数还是继承过来的虚函数)才会具有虚函数表。通常,虚函数表的首地址将会被存入对象的最前面(在 32 位的操作系统中,存储地址是用 4 个字节,因此这个首地址就会占用对象的前四个字节的空间)。对象的最前端是 vtable,虚函数表的内容是函数的入口地址。
之后定义了一个基类类型的指针 p,当通过 p 调用虚函数 v1 或 v2 时,系统会先去 p 所指向的对象的前四个字节中寻找到虚函数表地址,之后在内存中找到该虚函数表,然后在表中找到对应函数的入口地址,就可以访问这个函数。
当 p 指针指向的是基类对象时,基类的虚函数表将会被访问,基类中虚函数将会被调用。当 p 指针指向的是派生类对象时,访问的是派生类的虚函数表,派生类的虚函数表中存的是派生类中的虚函数入口地址,因此调用的是派生类中的虚函数。
使用多态会降低程序运行效率,使用多态的程序会使用更多的存储空间,存储虚函数表等内容,而且在调用函数时需要去虚函数表中查询函数入口地址,这会增加程序运行时间。在设计程序时,程序设计人员可以选择性的使用多态,对于有需要的函数使用多态,对于其它的函数则不要采用多态。