多态:

C++的多态是通过一张虚函数表(Virtual Table)来实现的,简称为 V-Table。在这个
表中,主要是一个类的虚函数的地址表,这张表解决了继承、覆写的问题,保证其真实
反应实际的函数。这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,
所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得由为重要了,
它就像一个地图一样,指明了实际所应该调用的函数

这里我们着重看一下这张虚函数表。C++的编译器应该是保证虚函数表的指针存在
于对象实例中最前面的位置(这是为了保证取到虚函数表的有最高的性能——如果有
多层继承或是多重继承的情况下)。 这意味着我们通过对象实例的地址得到这张虚函
数表,然后就可以遍历其中函数指针,并调用相应的函数

以下代码运行结果,基于环境 X86_64 64位编译器。

虚函数表与与表指针

#include <iostream>

using namespace std;

class Base {
public:
virtual void f() { cout << "Base::f" << endl; }
virtual void g() { cout << "Base::g" << endl; }
virtual void h() { cout << "Base::h" << endl; }
int ba;
}; typedef void(*FUNC)();
int main()
{
Base b;
cout<<sizeof(b)<<endl;
cout<<"Base_Addr:"<<(int*)(&b)<<endl;
cout<<"VTalbe_Addr:"<<(int**)(int*)(&b)<<endl;
FUNC pf = NULL;
pf = (FUNC)*((int**)*(int*)(&b)+0);
pf();
pf = (FUNC)*((int**)*(int*)(&b)+1);
pf();
pf = (FUNC)*((int**)*(int*)(&b)+2);
pf();
}

运行结果:

虚函数表排在一个类的最前面,虚函数在虚函数表中按声明顺序排列。

派生类部分实现覆写:

class Base {
public:
  virtual void f() { cout << "Base::f" << endl; }
  virtual void g() { cout << "Base::g" << endl; }
  virtual void h() { cout << "Base::h" << endl; }
}; class Derived:public Base
{
public:
  virtual void f() { cout<<"Derived::f"<<endl; }
}; typedef void(*FUNC)();
int main()
{
Derived b;
cout<<sizeof(b)<<endl;
cout<<"Base_Addr:"<<(int*)(&b)<<endl;
cout<<"VTalbe_Addr:"<<(int**)(int*)(&b)<<endl;
FUNC pf = NULL;
pf = (FUNC)*((int**)*(int*)(&b)+0);
pf();
pf = (FUNC)*((int**)*(int*)(&b)+1);
pf();
pf = (FUNC)*((int**)*(int*)(&b)+2);
pf();
}

运行结果:

单继承:

父子中兼有虚函数

class Base {
public:
  virtual void f() { cout << "Base::f" << endl; }
  virtual void g() { cout << "Base::g" << endl; }
  virtual void h() { cout << "Base::h" << endl; }
protected:
//int b;
}; class Derived:public Base
{
public:
virtual void d() { cout << "Derived::d" << endl; }

};
typedef void(*FUNC)();
int main()
{
Derived d;
cout<<sizeof(d)<<endl;
cout<<"Base_Addr:"<<(int*)(&d)<<endl;
cout<<"VTalbe_Addr:"<<(int**)(int*)(&b)<<endl;
FUNC pf = NULL;
pf = (FUNC)*((int**)*(int*)(&d)+0);
pf();
pf = (FUNC)*((int**)*(int*)(&d)+1);
pf();
pf = (FUNC)*((int**)*(int*)(&d)+2);
pf(); pf = (FUNC)*((int**)*(int*)(&d)+3);
pf(); Base *pb = & d;
pf = (FUNC)*((int**)*(int*)(pb)+3);
pf();
}

运行结果:

虚函数表形式

 总结:单继承体系中,派生类继承基类,并且有自己独立的虚函数,基类与派生类的虚函数表顺序存放在一张表中,派生类的虚函数表在基类后面,类中的成员变量不影响虚函数表的排布。

多继承:

基类中未完全覆写,派生类中又有虚函数

#include <iostream>

using namespace std;

class Base1 {
public:
virtual void f() { cout << "Base::f" << endl; }
virtual void g() { cout << "Base::g" << endl; }
virtual void h() { cout << "Base::h" << endl; }
}; class Base2 {
public:
virtual void f2() { cout << "Base::f2" << endl; }
virtual void g2() { cout << "Base::g2" << endl; }
virtual void h2() { cout << "Base::h2" << endl; }
}; class Derived:public Base1,public Base2
{
public:
void f() { cout << "Derived::f" << endl; } //override
virtual void i() { cout << "Derived::i" << endl; }
}; typedef void(*FUNC)();
int main()
{
Derived d;
   cout<<sizeof(d)<<endl;
Base1 * p1d = &d;
Base2 * p2d = &d; cout<<"p1d:"<<p1d<<endl;
cout<<"p2d:"<<p2d<<endl; FUNC pf = NULL;
pf = (FUNC)*((int**)*(int*)(p1d)+0);
pf();
pf = (FUNC)*((int**)*(int*)(p1d)+1);
pf();
pf = (FUNC)*((int**)*(int*)(p1d)+2);
pf();
pf = (FUNC)*((int**)*(int*)(p1d)+3);
pf();
   cout<<"-------------------------"<<endl; pf = (FUNC)*((int**)*(int*)(p2d)+0);
pf();
pf = (FUNC)*((int**)*(int*)(p2d)+1);
pf();
pf = (FUNC)*((int**)*(int*)(p2d)+2);
pf();
// pf = (FUNC)*((int**)*(int*)(p2d)+3); //crash// pf();
}

运行结果

继承关系:

虚函数表:

 结论:多继承中,每个基类都有自己的虚函数表,他们是独立存放的,如果派生类中有虚函数,那么虚函数表将与第一个继承的基类虚函数表顺序存放(合并)。

最新文章

  1. 用jquery写循环播放div的相关笔记 珍贵的总结 -1
  2. Linux下添加用户及用户组
  3. linux 小喇叭 没了
  4. respondsToSelector: selector
  5. hdu 1309 Loansome Car Buyer
  6. [转] linux下的c/c++调试器gdb
  7. php 版本比较
  8. 项目角度谈矢量切片运用以及Geoserver处理自定义规格矢量切片方案
  9. Linux 学习 (九) 网络基础
  10. 004_后端js编写工具
  11. U3D学习13-数据存储
  12. 转 这种方法可以免去自己计算大文件md5 的麻烦
  13. 让UI设计师崩溃的瞬间,你经历过哪些?
  14. AWS CSAA -- 04 AWS Object Storage and CDN - S3 Glacier and CloudFront(二)
  15. iPhone上将短信内容发送到指定邮箱的方法
  16. python类内init外声明的属性与init内声明的对象属性的访问和操作区别
  17. 【wireshark】插件开发(四):Lua插件Post-dissector和Listener
  18. 垂直方向兼容显示的内容多少的情况样式Flex布局
  19. redis 常用的server的命令
  20. 贪心:zoj3953 Intervals

热门文章

  1. VS2013产品密钥
  2. File类与常用IO流第六章——使用try...catch...finally处理流中的异常
  3. 【经典结构】单例模式Singleton
  4. C++中dynamic_cast与static_cast浅析与实例演示
  5. Java集合 - 初始化写法
  6. [考试总结]noip模拟7
  7. python 处理protobuf 接口常见错误
  8. 记intouch SMC local下驱动丢失问题解决
  9. 最高级的AutoHotkey重试源代码结构20191221.docx
  10. pytest框架fixture的使用