本文是基于32位系统下的分析,用的vs2019
参考资料:https://blog.csdn.net/haoel/article/details/1948051/
准备工作
vs 设置查看内存分布方式:
右键-> 属性 -> C/C++ 命令行 填入 /d1 reportAllClassLayout
一般继承(无虚函数覆盖)
class A
{
public:
virtual void f() { cout << "A::f()" << endl; }
int _a;
};
class B : public A
{
public:
void fb() { cout << "B::fb()" << endl; }
int _b;
};
vs打印出的内存布局如下图
内容复制出来如下
1>class A size(8):
1> ±–
1> 0 | {vfptr}
1> 4 | _a
1> ±–
1>A::$vftable@:
1> | &A_meta
1> | 0
1> 0 | &A::f
1> 1 | &A::g
1> 2 | &A::h
可以看到A类有一个4字节的虚函数指针vfptr,该虚函数指针指向了一张虚函数表vftable,虚函数表中存储了每一个虚函数的地址
1>class B size(12):
1> ±–
1> 0 | ±-- (base class A)
1> 0 | | {vfptr}
1> 4 | | _a
1> | ±–
1> 8 | _b
1> ±–
1>B::$vftable@:
1> | &B_meta
1> | 0
1> 0 | &A::f
1> 1 | &A::g
1> 2 | &A::h
B类继承了A类虚函数表和成员变量
一般继承(有虚函数覆盖,子类没有自己新的虚函数)
例:在子类B中重写父类A的f()方法
class A
{
public:
virtual void f() { cout << "A::f()" << endl; }
virtual void g() { cout << "A::g()" << endl; }
virtual void h() { cout << "A::h()" << endl; }
int _a;
};
class B : public A
{
public:
void f() { cout << "B::f()" << endl; }
int _b;
};
1>class B size(12):
1> ±–
1> 0 | ±-- (base class A)
1> 0 | | {vfptr}
1> 4 | | _a
1> | ±–
1> 8 | _b
1> ±–
1>B::$vftable@:
1> | &B_meta
1> | 0
1> 0 | &B::f
1> 1 | &A::g
1> 2 | &A::h
可见B的虚函数表中&B::f 覆盖了原有的&A::f
一般继承(有虚函数覆盖,子类有自己新的虚函数)
例:在子类B中重写父类A的f()方法,同时新增一个自己的虚函数k()
class A
{
public:
virtual void f() { cout << "A::f()" << endl; }
virtual void g() { cout << "A::g()" << endl; }
virtual void h() { cout << "A::h()" << endl; }
int _a;
};
class B : public A
{
public:
void f() { cout << "B::f()" << endl; }
virtual void k() { cout << "B::k()" << endl; }
int _b;
};
1>class B size(12):
1> ±–
1> 0 | ±-- (base class A)
1> 0 | | {vfptr}
1> 4 | | _a
1> | ±–
1> 8 | _b
1> ±–
1>B::$vftable@:
1> | &B_meta
1> | 0
1> 0 | &B::f
1> 1 | &A::g
1> 2 | &A::h
1> 3 | &B::k
可以看到新增的虚函数会顺式添加到已有的虚函数表中
多重继承(无虚函数覆盖)
class Base1
{
public:
Base1() : _iBase1(10) {}
virtual void f() { std::cout << "Base1::f()" << std::endl; }
virtual void g() { std::cout << "Base1::g()" << std::endl; }
virtual void h() { std::cout << "Base1::h()" << std::endl; }
int _iBase1;
};
class Base2
{
public:
Base2() : _iBase2(100) {}
virtual void f() { std::cout << "Base2::f()" << std::endl; }
virtual void g() { std::cout << "Base2::g()" << std::endl; }
virtual void h() { std::cout << "Base2::h()" << std::endl; }
int _iBase2;
};
class Base3
{
public:
Base3() : _iBase3(1000) {}
virtual void f() { std::cout << "Base3::f()" << std::endl; }
virtual void g() { std::cout << "Base3::g()" << std::endl; }
virtual void h() { std::cout << "Base3::h()" << std::endl; }
int _iBase3;
};
class Derive
: public Base1
, public Base2
, public Base3
{
public:
Derive() : _iDerived(10000) {}
virtual void f1() { std::cout << "Derive::f()" << std::endl; }
virtual void g1() { std::cout << "Derive::g1()" << std::endl; }
int _iDerived;
};
1>class Derive size(28):
1> ±–
1> 0 | ±-- (base class Base1)
1> 0 | | {vfptr}
1> 4 | | _iBase1
1> | ±–
1> 8 | ±-- (base class Base2)
1> 8 | | {vfptr}
1>12 | | _iBase2
1> | ±–
1>16 | ±-- (base class Base3)
1>16 | | {vfptr}
1>20 | | _iBase3
1> | ±–
1>24 | _iDerived
1> ±–
1>Derive::KaTeX parse error: Expected 'EOF', got '&' at position 22: …e@Base1@: 1> | &̲Derive_meta 1> …vftable@Base2@:
1> | -8
1> 0 | &Base2::f
1> 1 | &Base2::g
1> 2 | &Base2::h
1>Derive::$vftable@Base3@:
1> | -16
1> 0 | &Base3::f
1> 1 | &Base3::g
1> 2 | &Base3::h
总结:多重继承(无虚函数覆盖)场景
①每个父类都有自己的虚表
②派生类中的布局按照基类被声明时的顺序进行排列
③派生类自己的虚函数,会被加入到第一个基类的虚函数表中
多重继承(有虚函数覆盖)
class Base1
{
public:
Base1() : _iBase1(10) {}
virtual void f() { std::cout << "Base1::f()" << std::endl; }
virtual void g() { std::cout << "Base1::g()" << std::endl; }
virtual void h() { std::cout << "Base1::h()" << std::endl; }
int _iBase1;
};
class Base2
{
public:
Base2() : _iBase2(100) {}
virtual void f() { std::cout << "Base2::f()" << std::endl; }
virtual void g() { std::cout << "Base2::g()" << std::endl; }
virtual void h() { std::cout << "Base2::h()" << std::endl; }
int _iBase2;
};
class Base3
{
public:
Base3() : _iBase3(1000) {}
virtual void f() { std::cout << "Base3::f()" << std::endl; }
virtual void g() { std::cout << "Base3::g()" << std::endl; }
virtual void h() { std::cout << "Base3::h()" << std::endl; }
int _iBase3;
};
class Derive
: public Base1
, public Base2
, public Base3
{
public:
Derive() : _iDerived(10000) {}
void f() { std::cout << "Derive::f()" << std::endl; }
virtual void g1() { std::cout << "Derive::g1()" << std::endl; }
int _iDerived;
};
1>class Derive size(28):
1> ±–
1> 0 | ±-- (base class Base1)
1> 0 | | {vfptr}
1> 4 | | _iBase1
1> | ±–
1> 8 | ±-- (base class Base2)
1> 8 | | {vfptr}
1>12 | | _iBase2
1> | ±–
1>16 | ±-- (base class Base3)
1>16 | | {vfptr}
1>20 | | _iBase3
1> | ±–
1>24 | _iDerived
1> ±–
1>Derive::KaTeX parse error: Expected 'EOF', got '&' at position 22: …e@Base1@: 1> | &̲Derive_meta 1> …vftable@Base2@:
1> | -8
1> 0 | &thunk: this-=8; goto Derive::f // 这里存的不是&Derive::f,而是跳转指令
1> 1 | &Base2::g
1> 2 | &Base2::h
1>Derive::$vftable@Base3@:
1> | -16
1> 0 | &thunk: this-=16; goto Derive::f // 这里存的不是&Derive::f,而是跳转指令
1> 1 | &Base3::g
1> 2 | &Base3::h
可以看到内存分布中Derive继承了Base1 Base2 Base3的虚函数指针、虚函数表,同时按照基类Base1、Base2、Base3的声明顺序进行排列;Derive重写了f()函数,因此Base1 Base2 Base3的需表中各自的f()函数都被覆盖成&Derive::f,不过只有Base1的虚表中存的是&Derive::f,Base2 Base3虚表中都是一条跳转指令。Derive自己新增的虚函数被存放在
Base1的虚表中。
总结:多重继承(有虚函数覆盖)场景
①每个父类都有自己的虚表
②派生类中的布局按照基类被声明时的顺序进行排列
③派生类自己的虚函数,会被加入到第一个基类的虚函数表中
④派生类会覆盖基类的虚函数,但是只有第一个虚表中存放的是被覆盖的函数地址,其他的都存放的是跳转指令
一个简单的例子测试虚表的存在
#include <iostream>
class A
{
public:
A() :_a(10) {}
virtual void f() { std::cout << "A::f()" << std::endl; }
virtual void g() { std::cout << "A::g()" << std::endl; }
virtual void h() { std::cout << "A::h()" << std::endl; }
int _a;
};
class B : public A
{
public:
B() :_b(100) {}
void f() { std::cout << "B::f()" << std::endl; }
int _b;
};
typedef void(*Function)(void);
int main(void)
{
B b;
#if 1
int* vfptr = (int*)&b;
int vtaleAdress = *vfptr;
int* pVtable = (int*)vtaleAdress;
int func = *pVtable;
Function f = (Function)func;
f();
Function g = (Function) * (pVtable + 1);
g();
Function h = (Function) * (pVtable + 2);
h();
std::cout << "A::_a = " << (int)*(vfptr + 1) << std::endl;
std::cout << "B::_b = " << (int)*(vfptr + 2) << std::endl;
#endif
#if 0
int** vfptr = (int**)&b;
for (int idx = 0; vfptr[0][idx] != NULL; ++idx) {
Function f = (Function)vfptr[0][idx];
f();
}
std::cout << "A::_a = " << (int)vfptr[1] << std::endl;
std::cout << "B::_b = " << (int)vfptr[2] << std::endl;
#endif
return 0;
}