系列
c++内存分布之虚函数(多继承) 【本文】
先声明、先存储、先父类、再子类
的顺序存放类的成员变量其实,覆盖继承不够准确。
需要考虑是否有虚函数的情况
父类和派生类出现了同名虚函数函数((普通虚函数、纯虚函数),派生类的虚函数表中将子类的同名虚函数的地址替换为自身的同名虚函数的地址-------多态出现
父类和派生类同时出现同名成员函数,这与成员变量覆盖继承的情况是一样的,派生类屏蔽父类的同名函数
序号
情况(多继承,基类个数:2)
1
基类均无虚函数(A,派生类有虚函数,B,派生类不含有虚函数)
2
两个基类中只有一个类有虚函数(A,派生类有虚函数,B,派生类不含有虚函数)
3
两个基类中都含有虚函数(A,派生类有虚函数,B,派生类不含有虚函数)
// 基类
class baseA
{
public:
int _ma = 1;
int _mb = 2;
};
class baseB
{
public:
int _mc = 3;
int _md = 4;
};
// 派生类
class deriveA : public baseA, public baseB
{
public:
virtual void print() { std::cout << "deriveA::print()\n\n\n"; }
int _me = 3;
int _mf = 4;
};
1>class deriveA size(28):
1> +---
1> 0 | {vfptr}
1> 4 | +--- (base class baseA)
1> 4 | | _ma
1> 8 | | _mb
1> | +---
1>12 | +--- (base class baseB)
1>12 | | _mc
1>16 | | _md
1> | +---
1>20 | _me
1>24 | _mf
1> +---
1>
1>deriveA::$vftable@:
1> | &deriveA_meta
1> | 0
1> 0 | &deriveA::print
28字节 = 基类和派生类总共六个int成员变量 + 虚函数表指针4字节
// 基类
class baseA
{
public:
int _ma = 1;
int _mb = 2;
};
class baseB
{
public:
int _mc = 3;
int _md = 4;
};
// 派生类
class deriveA : public baseA, public baseB
{
public:
int _me = 3;
int _mf = 4;
};
1>class deriveA size(24):
1> +---
1> 0 | +--- (base class baseA)
1> 0 | | _ma
1> 4 | | _mb
1> | +---
1> 8 | +--- (base class baseB)
1> 8 | | _mc
1>12 | | _md
1> | +---
1>16 | _me
1>20 | _mf
1> +---
24字节= 基类总4个int成员变量 + 派生类的2个int成员变量 = 4 * 4 + 4 * 2 = 24
// 基类
class baseA
{
public:
virtual void show() { std::cout << "virtual baseA::show() \n\n"; }
int _ma = 1;
int _mb = 2;
};
class baseB
{
public:
int _mc = 3;
int _md = 4;
};
// 派生类
class deriveA : public baseA, public baseB
{
public:
virtual void print() { std::cout << "virtual deriveA::print() \n\n"; }
int _me = 3;
int _mf = 4;
};
1>class deriveA size(28):
1> +---
1> 0 | +--- (base class baseA)
1> 0 | | {vfptr}
1> 4 | | _ma
1> 8 | | _mb
1> | +---
1>12 | +--- (base class baseB)
1>12 | | _mc
1>16 | | _md
1> | +---
1>20 | _me
1>24 | _mf
1> +---
1>
1>deriveA::$vftable@:
1> | &deriveA_meta
1> | 0
1> 0 | &baseA::show
1> 1 | &deriveA::print
虚函数表指针。
虚函数表。
28字节 = 基类总共4个int成员变量 + 派生类的2个int成员变量 + 一个虚函数表指针4字节
代码
// 基类
class baseA
{
public:
virtual void show() { std::cout << "virtual baseA::show() \n\n"; }
int _ma = 1;
int _mb = 2;
};
class baseB
{
public:
int _mc = 3;
int _md = 4;
};
// 派生类
class deriveA : public baseB, public baseA
{
public:
virtual void print() { std::cout << "virtual deriveA::print() \n\n"; }
int _me = 3;
int _mf = 4;
};
内存分布
1>class deriveA size(28):
1> +---
1> 0 | +--- (base class baseA)
1> 0 | | {vfptr}
1> 4 | | _ma
1> 8 | | _mb
1> | +---
1>12 | +--- (base class baseB)
1>12 | | _mc
1>16 | | _md
1> | +---
1>20 | _me
1>24 | _mf
1> +---
1>
1>deriveA::$vftable@:
1> | &deriveA_meta
1> | 0
1> 0 | &baseA::show
1> 1 | &deriveA::print
1>
1>deriveA::print this adjustor: 0
你肯定也发现了。这与2.1.2内存布局的情况是一摸一样的。
存储顺序:先基类,再派生,先声明,先存储。由于存在虚函数,所以,这个需要优先考虑。
尽管baseB更靠近派生类,但是baseA的优先级更高,因为基类baseA存在虚函数而基类baseB不存在。
虚表依然仅按照先声明像存储的顺序存储虚函数。基类优先级 > 派生类优先级。
2.1.5.3 sizeof
与 2.1.3 sizeof相同。因为没有成员变量增加和减少。
// 基类
class baseA
{
public:
virtual void show() { std::cout << "virtual baseA::show() \n\n"; }
int _ma = 1;
int _mb = 2;
};
class baseB
{
public:
int _mc = 3;
int _md = 4;
};
// 派生类
class deriveA : public baseA, public baseB
{
public:
int _me = 3;
int _mf = 4;
};
1>class deriveA size(28):
1> +---
1> 0 | +--- (base class baseA)
1> 0 | | {vfptr}
1> 4 | | _ma
1> 8 | | _mb
1> | +---
1>12 | +--- (base class baseB)
1>12 | | _mc
1>16 | | _md
1> | +---
1>20 | _me
1>24 | _mf
1> +---
1>
1>deriveA::$vftable@:
1> | &deriveA_meta
1> | 0
1> 0 | &baseA::show
代码
class deriveA : public baseB, public baseA
{
…..
}
内存分布
1>
1>class deriveA size(28):
1> +---
1> 0 | +--- (base class baseA)
1> 0 | | {vfptr}
1> 4 | | _ma
1> 8 | | _mb
1> | +---
1>12 | +--- (base class baseB)
1>12 | | _mc
1>16 | | _md
1> | +---
1>20 | _me
1>24 | _mf
1> +---
1>
1>deriveA::$vftable@:
1> | &deriveA_meta
1> | 0
1> 0 | &baseA::show
你肯定又发现了,这与上面的内存分布情况是一样的。
为什么?因为基类有两个,且只有其中一个存在虚函数,所以,按照先声明存储的规则,且考虑到有虚函数的基类的优先级大于没有虚函数的基类。故,含有虚函数的类成员变量排在内存分布图的虚函数表指针的后面,而且是紧挨着。
// 基类
class baseA
{
public:
virtual void show() { std::cout << "virtual baseA::show() \n\n"; }
int _ma = 1;
int _mb = 2;
};
class baseB
{
public:
virtual void play() { std::cout << "virtual baseB::play() \n\n"; }
int _mc = 3;
int _md = 4;
};
// 派生类
class deriveA : public baseA, public baseB
{
public:
virtual void print() { std::cout << "deriveA::print()\n\n"; }
int _me = 3;
int _mf = 4;
};
1>class deriveA size(32):
1> +---
1> 0 | +--- (base class baseA)
1> 0 | | {vfptr}
1> 4 | | _ma
1> 8 | | _mb
1> | +---
1>12 | +--- (base class baseB)
1>12 | | {vfptr}
1>16 | | _mc
1>20 | | _md
1> | +---
1>24 | _me
1>28 | _mf
1> +---
1>
1>deriveA::$vftable@baseA@:
1> | &deriveA_meta
1> | 0
1> 0 | &baseA::show
1> 1 | &deriveA::print
1>
1>deriveA::$vftable@baseB@:
1> | -12
1> 0 | &baseB::play
1>
1>deriveA::print this adjustor: 0
等等,你是不是也发现了,内存模型中出现了2个{vfptr}。一个属于基类baseA,另一个属于基类baseB。
虚函数表。 发现了吧:1.派生类的虚函数表多了一张表,2.派生类的虚函数是放在第一张虚函数表中。
按照先前的顺序:先基类,再派生,先声明,先存储。但是有虚函数的类要优先考虑。这里基类baseA和baseB还有派生类都含有虚函数。那么先看基类,按照先声明先存储的顺序,baseA基类相对baseB基类先声明,故基类baseA的虚函数表指针首先被存储,接着再是基类baseA的成员变量,然后是基类baseB的虚函数表指针,基类baseB的成员变量。最后是派生类。
为什么派生类的虚函数是追加在第一张虚表的后面? 请看下面的一段汇编(没学过汇编,不献丑)结论: 派生类的虚函数是追加在第一张虚表的后面。当需要使用派生类的虚函数是,用第一张表的虚函数表指针指向派生类的虚函数即可。(个人观点)下面的汇编也应该是这样:1,找到虚函数表的起始地址,2.找到派生类的虚函数偏移,3.使用虚函数表指针指向派生类的虚函数。
deriveA *pda = &da;
00A7A02E lea eax,[ebp-28h]
00A7A031 mov dword ptr [ebp-34h],eax
pda->print();
00A7A034 mov eax,dword ptr [ebp-34h]
00A7A037 mov edx,dword ptr [eax]
00A7A039 mov esi,esp
00A7A03B mov ecx,dword ptr [ebp-34h]
00A7A03E mov eax,dword ptr [edx+4]
00A7A041 call eax
00A7A043 cmp esi,esp
00A7A045 call 00A714C4
32字节 = 基类总共4个int成员变量 + 派生类的2个int变量 + 2个虚函数表指针 = 4 * 4 + 2 * 4 + 2 * 4 = 32
监视结果中看不到派生类的虚函数存储情况。
// 基类
class baseA
{
public:
virtual void show() { std::cout << "virtual baseA::show() \n\n"; }
int _ma = 1;
int _mb = 2;
};
// 基类
class baseB
{
public:
virtual void play() { std::cout << "virtual baseB::play() \n\n"; }
int _mc = 1;
int _md = 2;
};
// 派生类
class deriveA : public baseA , public baseB
{
public:
int _me = 3;
int _mf = 4;
};
1>class deriveA size(32):
1> +---
1> 0 | +--- (base class baseA)
1> 0 | | {vfptr}
1> 4 | | _ma
1> 8 | | _mb
1> | +---
1>12 | +--- (base class baseB)
1>12 | | {vfptr}
1>16 | | _mc
1>20 | | _md
1> | +---
1>24 | _me
1>28 | _mf
1> +---
1>
1>deriveA::$vftable@baseA@:
1> | &deriveA_meta
1> | 0
1> 0 | &baseA::show
1>
1>deriveA::$vftable@baseB@:
1> | -12
1> 0 | &baseB::play
// 基类
class baseA
{
public:
virtual void show() { std::cout << "virtual baseA::show() \n\n"; }
int _ma = 1;
int _mb = 2;
};
class baseB
{
public:
virtual void play() { std::cout << "virtual baseB::play() \n\n"; }
int _mc = 3;
int _md = 4;
};
class baseC
{
public:
virtual void listening() { std::cout << "virtual baseC::listening() \n\n"; }
int _mh = 7;
int _mi = 8;
};
// 派生类
class deriveA : public baseA, public baseB, public baseC
{
public:
int _me = 3;
int _mf = 4;
};
1>class deriveA size(44):
1> +---
1> 0 | +--- (base class baseA)
1> 0 | | {vfptr}
1> 4 | | _ma
1> 8 | | _mb
1> | +---
1>12 | +--- (base class baseB)
1>12 | | {vfptr}
1>16 | | _mc
1>20 | | _md
1> | +---
1>24 | +--- (base class baseC)
1>24 | | {vfptr}
1>28 | | _mh
1>32 | | _mi
1> | +---
1>36 | _me
1>40 | _mf
1> +---
1>
1>deriveA::$vftable@baseA@:
1> | &deriveA_meta
1> | 0
1> 0 | &baseA::show
1>
1>deriveA::$vftable@baseB@:
1> | -12
1> 0 | &baseB::play
1>
1>deriveA::$vftable@baseC@:
1> | -24
1> 0 | &baseC::listening
// 基类
class baseA
{
public:
virtual void show() { std::cout << "virtual baseA::show() \n\n"; }
int _ma = 1;
int _mb = 2;
};
class baseB
{
public:
virtual void play() { std::cout << "virtual baseB::play() \n\n"; }
int _mc = 3;
int _md = 4;
};
class baseC
{
public:
virtual void listening() { std::cout << "virtual baseC::listening() \n\n"; }
int _mh = 7;
int _mi = 8;
};
// 派生类
class deriveA : public baseA, public baseB, public baseC
{
public:
virtual void show() { std::cout << "virtual deriveA::show() \n\n"; }
int _me = 3;
int _mf = 4;
};
1>class deriveA size(44):
1> +---
1> 0 | +--- (base class baseA)
1> 0 | | {vfptr}
1> 4 | | _ma
1> 8 | | _mb
1> | +---
1>12 | +--- (base class baseB)
1>12 | | {vfptr}
1>16 | | _mc
1>20 | | _md
1> | +---
1>24 | +--- (base class baseC)
1>24 | | {vfptr}
1>28 | | _mh
1>32 | | _mi
1> | +---
1>36 | _me
1>40 | _mf
1> +---
1>
1>deriveA::$vftable@baseA@:
1> | &deriveA_meta
1> | 0
1> 0 | &deriveA::show
1>
1>deriveA::$vftable@baseB@:
1> | -12
1> 0 | &baseB::play
1>
1>deriveA::$vftable@baseC@:
1> | -24
1> 0 | &baseC::listening
1>
1>deriveA::show this adjustor: 0
// 基类
class baseA
{
public:
int _ma = 1;
int _mb = 2;
};
class baseB
{
public:
virtual void play() { std::cout << "virtual baseB::play() \n\n"; }
int _mc = 3;
int _md = 4;
};
class baseC
{
public:
virtual void listening() { std::cout << "virtual baseC::listening() \n\n"; }
int _mh = 7;
int _mi = 8;
};
// 派生类
class deriveA : public baseA, public baseB, public baseC
{
public:
int _me = 3;
int _mf = 4;
};
1>class deriveA size(40):
1> +---
1> 0 | +--- (base class baseB)
1> 0 | | {vfptr}
1> 4 | | _mc
1> 8 | | _md
1> | +---
1>12 | +--- (base class baseC)
1>12 | | {vfptr}
1>16 | | _mh
1>20 | | _mi
1> | +---
1>24 | +--- (base class baseA)
1>24 | | _ma
1>28 | | _mb
1> | +---
1>32 | _me
1>36 | _mf
1> +---
1>
1>deriveA::$vftable@baseB@:
1> | &deriveA_meta
1> | 0
1> 0 | &baseB::play
1>
1>deriveA::$vftable@baseC@:
1> | -12
1> 0 | &baseC::listening
// 基类
class baseA
{
public:
// virtual void show() { std::cout << "virtual baseA::show() \n\n"; }
int _ma = 1;
int _mb = 2;
};
class baseB
{
public:
virtual void play() { std::cout << "virtual baseB::play() \n\n"; }
int _mc = 3;
int _md = 4;
};
class baseC
{
public:
virtual void listening() { std::cout << "virtual baseC::listening() \n\n"; }
int _mh = 7;
int _mi = 8;
};
// 派生类
class deriveA : public baseA, public baseB, public baseC
{
public:
virtual void show() { std::cout << "virtual deriveA::show() \n\n"; }
int _me = 3;
int _mf = 4;
};
1>
1>class deriveA size(40):
1> +---
1> 0 | +--- (base class baseB)
1> 0 | | {vfptr}
1> 4 | | _mc
1> 8 | | _md
1> | +---
1>12 | +--- (base class baseC)
1>12 | | {vfptr}
1>16 | | _mh
1>20 | | _mi
1> | +---
1>24 | +--- (base class baseA)
1>24 | | _ma
1>28 | | _mb
1> | +---
1>32 | _me
1>36 | _mf
1> +---
1>
1>deriveA::$vftable@baseB@:
1> | &deriveA_meta
1> | 0
1> 0 | &baseB::play
1> 1 | &deriveA::show
1>
1>deriveA::$vftable@baseC@:
1> | -12
1> 0 | &baseC::listening
1>
1>deriveA::show this adjustor: 0
手机扫一扫
移动阅读更方便
你可能感兴趣的文章