1.多继承无函数覆盖
struct Base1
{
int a;
int b;
public:
Base1()
{
a = 1;
b = 2;
}
virtual void Fn_1()
{
printf("Base1:Fn_1...\n");
}
virtual void Fn_2()
{
printf("Base1:Fn_2...\n");
}
};
struct Base2
{
int a;
int b;
public:
Base2()
{
a = 3;
b = 4;
}
virtual void Fn_3()
{
printf("Base2:Fn_3...\n");
}
virtual void Fn_4()
{
printf("Base2:Fn_4...\n");
}
};
struct Sub:Base1,Base2
{
int a;
int b;
public:
Sub()
{
a = 5;
b = 6;
}
virtual void Fn_5()
{
printf("Sub:Fn_5...\n");
}
virtual void Fn_6()
{
printf("Sub:Fn_6...\n");
}
};
int main(int argc, char* argv[])
{
//查看 Sub 的虚函数表
Sub sub;
//通过函数指针调用函数,验证正确性
typedef void(*pFunction)(void);
//对象的前四个字节是第一个Base1的虚表
printf("Sub 的虚函数表地址为:%x\n",*(int*)&sub);
pFunction pFn;
for(int i=0;i<6;i++)
{
int temp = *((int*)(*(int*)&sub)+i);
if(temp == 0)
{
break;
}
pFn = (pFunction)temp;
pFn();
}
//对象的第4个四字节是Base2的虚表
printf("Sub 的虚函数表地址为:%x\n",*(int*)((int)&sub+0xC));
pFunction pFn1;
for(int k=0;k<2;k++)
{
int temp = *((int*)(*(int*)((int)&sub+0xC))+k);
pFn1 = (pFunction)temp;
pFn1();
}
return 0;
}
总结:
有几个父类就有几个表
子类的虚函数放在第一个表
2.多继承有函数覆盖
struct Base1
{
public:
virtual void Fn_1()
{
printf("Base1:Fn_1...\n");
}
virtual void Fn_2()
{
printf("Base1:Fn_2...\n");
}
};
struct Base2
{
public:
virtual void Fn_3()
{
printf("Base2:Fn_3...\n");
}
virtual void Fn_4()
{
printf("Base2:Fn_4...\n");
}
};
struct Sub:Base1,Base2
{
public:
virtual void Fn_1()
{
printf("Sub:Fn_1...\n");
}
virtual void Fn_3()
{
printf("Sub:Fn_3...\n");
}
virtual void Fn_5()
{
printf("Sub:Fn_5...\n");
}
};
int main(int argc, char* argv[])
{
//查看 Sub 的虚函数表
Sub sub;
//通过函数指针调用函数,验证正确性
typedef void(*pFunction)(void);
//对象的前四个字节是第一个Base1的虚表
printf("Sub 的虚函数表地址为:%x\n",*(int*)&sub);
pFunction pFn;
for(int i=0;i<6;i++)
{
int temp = *((int*)(*(int*)&sub)+i);
if(temp == 0)
{
break;
}
pFn = (pFunction)temp;
pFn();
}
//对象的第二个四字节是Base2的虚表
printf("Sub 的虚函数表地址为:%x\n",*(int*)((int)&sub+4));
pFunction pFn1;
for(int k=0;k<2;k++)
{
int temp = *((int*)(*(int*)((int)&sub+4))+k);
pFn1 = (pFunction)temp;
pFn1();
}
return 0;
}
总结:
3.多层继承有函数覆盖
struct Base1
{
public:
virtual void Fn_1()
{
printf("Base1:Fn_1...\n");
}
virtual void Fn_2()
{
printf("Base1:Fn_2...\n");
}
};
struct Base2:Base1
{
public:
virtual void Fn_3()
{
printf("Base2:Fn_3...\n");
}
};
struct Sub:Base2
{
public:
virtual void Fn_1()
{
printf("Sub:Fn_1...\n");
}
virtual void Fn_3()
{
printf("Sub:Fn_3...\n");
}
};
int main(int argc, char* argv[])
{
//查看 Sub 的虚函数表
Sub sub;
//观察大小:虚函数表只有一个
printf("%x\n",sizeof(sub));
//通过函数指针调用函数,验证正确性
typedef void(*pFunction)(void);
//对象的前四个字节是就是虚函数表
printf("Sub 的虚函数表地址为:%x\n",*(int*)&sub);
pFunction pFn;
for(int i=0;i<6;i++)
{
int temp = *((int*)(*(int*)&sub)+i);
if(temp == 0)
{
break;
}
pFn = (pFunction)temp;
pFn();
}
return 0;
}
总结:从上往下最后一个重写函数才会写入虚函数表
4.前期绑定与动态绑定
绑定:就是将函数调用与地址关联起来。
前期绑定:又称编译期绑定,即编译时已将函数地址写入,E8直接call
后期绑定:又称动态绑定、运行期绑定,即编译时没有直接写入地址,而是FF间接call
多态:相同的调用产生不同的行为
class Base
{
public:
int x;
public:
Base()
{
x = 100;
}
void Function_1()
{
printf("Base:Function_1...\n");
}
virtual void Function_2()
{
printf("Base:Function_2...virtual\n");
}
};
class Sub:public Base
{
public:
int x;
public:
Sub()
{
x = 200;
}
void Function_1()
{
printf("Sub:Function_1...\n");
}
virtual void Function_2()
{
printf("Sub:Function_2...virtual\n");
}
};
void TestBound(Base* pb)
{
int n = pb->x;
printf("%x\n",n);
pb->Function_1(); //函数调用
pb->Function_2();
}
int main(int argc, char* argv[])
{
Base base;//Sub base;
TestBound(&base);
return 0;
}
当传入对象为Sub指针
当传入对象为Base指针
运行结果:
Base base;
Sub base;
总结:
- 只有virtual的函数是动态绑定.
- 动态绑定还有一个名字:多态
5.作业
#include<string>
#include <iostream>
using namespace std;
class Base
{
public:
int x;
int y;
public:
Base()
{
x = 1;
y = 2;
}
void Print()
{
printf("Base:%x %x\n",x,y);
}
};
class Sub1:public Base
{
public:
int A;
Sub1()
{
x = 4;
y = 5;
A = 6;
}
void Print()
{
printf("Sub1:%x %x %x\n",x,y,A);
}
};
class Sub2:public Base
{
public:
int B;
Sub2()
{
x = 7;
y = 8;
B = 9;
}
void Print()
{
printf("Sub2:%x %x %x\n",x,y,B);
}
};
class Sub3:public Base
{
public:
int C;
Sub3()
{
x = 10;
y = 11;
C = 12;
}
void Print()
{
printf("Sub3:%x %x %x\n",x,y,C);
}
};
void TestBound()
{
Base b;
Sub1 s1;
Sub2 s2;
Sub3 s3;
Base* arr[] = {&b,&s1,&s2,&s3};
for(int i=0;i<4;i++)
{
arr[i]->Print();
}
}
不写成virtual
写成virtual
思考题:为什么析构函数写成virtual?
定义成虚函数才能准确退出,不然类用完不知道调用的哪个析构函数