探究一下 C++ 虚表继承的实现。
class 关系
设计一个菱形继承关系,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| +------------+ | | | Base | | | +------------+ / \ / \ / \ / \ +------------+ +-------------+ | | | | | LeftDerive | | RightDerive | | | | | +------------+ +-------------+ \ / \ / \ / +------------+ | | | Boss | | | +------------+
|
class 的具体实现如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
| class Base{ public: Base(){ BasePriv2=20; BasePriv1=10; BaseProt1=1; } virtual int FunctionA()=0; virtual int FunctionB(){ puts("Base::FunctionB"); return 0; }; virtual int FunctionC()=0; virtual int FunctionD(){ puts("Base::FunctionD"); return 0; };
int NonVirtual(){ puts("Base::NonVirtual"); counter=1; return 0; }
int count(){return counter;};
protected: int counter=0; int BaseProt1; private: int BasePriv1; int BasePriv2; };
class LeftDerive: public Base{ public: LeftDerive(){ LeftProt2=2; LeftProt1=1; LeftPriv3=3; } int FunctionA() override{ printf("Left DeriveA\n"); counter=4; return 0; }
virtual int LeftV1(){ puts("Left V1"); return 0; } virtual int LeftV2()=0;
int FunctionB() override{ printf("Left B\n"); return 0; }
int FunctionC() override{ printf("Left C\n"); return 0; }
int FunctionD() override{ printf("Left D\n"); return 0; }
int NonVirtual(){ puts("LeftDerive::NonVirtual"); counter=3; return 0; }
protected: int LeftProt1; int LeftProt2; private: int LeftPriv3;
};
class RightDerive: public Base{ public: RightDerive(){ RightProt2=2; RightProt1=1; RightPriv4=4; RightPriv5=5; RightPriv6=6; RightProt3=3; } int FunctionA() override { printf("Right Derive\n"); counter=5; return 0; }
int FunctionC() override{ printf("Right C\n"); return 0; }
int FunctionD() override{ printf("Right D\n"); return 0; }
int NonVirtual(){ puts("RightDerive::NonVirtual"); counter=6; return 0; }
virtual int RightV1(){ puts("Right V1"); return 0; }
virtual int RightV2(){ puts("Right V2"); return 0; }
protected: int RightProt1; int RightProt2; int RightProt3; private: int RightPriv4; int RightPriv5; int RightPriv6;
};
class Boss:public LeftDerive, public RightDerive{ public: Boss(){ printf("Boss() start,this=%p\n",this);
Init1=100; Init6=600; Init5=500; Init2=200; Init3=300; Init4="400";
printf("Boss() end\n");
} int FunctionA(){ printf("Boss Derive\n");
return 0; } int NonVirtual(){ puts("Boss::NonVirtual"); return 0; }
virtual int BossV1(){ puts("Boss V1"); return 0; }
int RightV1(){ puts("Boss:RightV1"); return 0; }
int LeftV2(){ puts("Boss:LeftV2"); return 0; } protected: int Init0; int Init1; int Init2; short Init3; const char * Init4; double Init5; char Init6; }; int main(){
Boss boss=Boss(); Boss * pBoss = &boss;
pBoss->FunctionA();
pBoss->BossV1(); pBoss->NonVirtual();
pBoss->LeftV1(); pBoss->LeftV2();
pBoss->RightV1(); pBoss->RightV2(); return 0; }
|
Base, LeftDerive, RightDerive 均存在虚函数、纯虚函数,同时加入了成员变量,方便观察继承的变量内存排布。
编译、反编译
使用 Clang 编译,使用 IDA Pro 7.5 反编译。
反编译 Boss 的构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| void __fastcall Boss::Boss(Boss *this) { LeftDerive::LeftDerive(this); RightDerive::RightDerive((Boss *)((char *)this + 40)); *(_QWORD *)this = off_100004038; *((_QWORD *)this + 5) = off_100004088; printf("Boss() start,this=%p\n", this); *((_DWORD *)this + 23) = 100; *((_BYTE *)this + 120) = 88; *((_QWORD *)this + 14) = 0x407F400000000000LL; *((_DWORD *)this + 24) = 200; *((_WORD *)this + 50) = 300; *((_QWORD *)this + 13) = "400"; printf("Boss() end\n"); }
|
按继承顺序,调用父类的构造函数(LeftDerive,RightDerive)
可以发现,相应的构造函数的参数不同,是每个父类对应的内存空间。
LeftDerive 的空间是 this,RightDerive 的空间是 this + 40
将每一个父类对应的虚表,覆盖到父类对应的内存空间的开始处(LeftDerive::vftable, RightDerive::vftable)
如果父类对应的内存空间不是 this,对于子类重写的虚函数,编译器会在子类的虚表加入这个虚函数,同时,在对应父类的虚表添加跳板函数(IDA 中显示的 non-virtual thunk
),将传入的 this 减去父类对应内存空间的偏移,再调用真正的虚函数。
1 2 3 4
| __int64 __fastcall `non-virtual thunk to'Boss::RightV1(Boss *this) { return Boss::RightV1((Boss *)((char *)this - 40)); }
|
子类没有重写的虚函数,编译器直接找到对应的虚表进行调用。