c++中虚多态的实现机制


參考博客:http://blog.csdn.net/neiloid/article/details/6934135

  1. 序言
  2. 证明vptr指针存在
  3. 无继承
  4. 单继承无覆盖
  5. 单继承有覆盖
  6. 多继承有覆盖
  7. 总结

1.序言

这篇博文探讨c++内部多态的实现机制,參考书主要是《深入探索c++对象模型》。因为本人水平有限,望读者指正。

实现多态有3点前提,即继承。虚化,基类指针指向派生类对象。

本质而言,整体来说,当类的内部通过virtual关键字声明虚函数时。编译器生成vptr指针,指向虚函数表(虚表),虚表中依次存放着各虚函数(非虚函数不在这里)。

这样。就能够通过指针来调用这些函数。

PS:执行环境为VC++6.0。因为编译器的差异而产生的不同本文不讨论。


2.证明vptr指针的存在

#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 main (void)
{
Base b;
cout<<"证明vptr的存在"<<endl;
cout<<"sizeof b = " <<sizeof b<<endl;
return 0;
}



ps:linux下 sizeof b = 8


3无继承

#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 main (void)
{
typedef unsigned long P_FUN;//指针类型为无符号长整型
typedef void(*FUN)(void);//声明函数指针 FUN f,g,h;
Base b; f=(FUN)(*(P_FUN*)*(P_FUN*)&b);
g=(FUN)(*((P_FUN*)*(P_FUN*)&b+1));
h=(FUN)(*((P_FUN*)*(P_FUN*)&b+2)); f();
g();
h(); cout<<(FUN)(*((P_FUN*)*(P_FUN*)&b+3))<<endl;
return 0;
}



这样就通过指针的方式调用了续表中的方法函数。

通过现象。能够大概预计出内存的布局。例如以下图。

结束位置以后可能为操作系统内部的仅仅读空间,訪问会发生段错误。

ps:linux下结束位置可能为0。可能为1,有兴趣的读着能够研究一下。


4.单继承无覆盖

#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;} };
class Derive:public Base
{
public:
virtual void f1(){cout<<"Derive::f1()"<<endl;}
virtual void g1(){cout<<"Derive::g1()"<<endl;}
virtual void h1(){cout<<"Derive::h1()"<<endl;} };
int main (void)
{
typedef unsigned long P_FUN;
typedef void(*FUN)(void); FUN f,g,h,f1,g1,h1;
Derive b; f=(FUN)(*(P_FUN*)*(P_FUN*)&b);
g=(FUN)(*((P_FUN*)*(P_FUN*)&b+1));
h=(FUN)(*((P_FUN*)*(P_FUN*)&b+2));
f1=(FUN)(*((P_FUN*)*(P_FUN*)&b+3));
g1=(FUN)(*((P_FUN*)*(P_FUN*)&b+4));
h1=(FUN)(*((P_FUN*)*(P_FUN*)&b+5)); f();
g();
h();
f1();
g1();
h1(); cout<<(FUN)(*((P_FUN*)*(P_FUN*)&b+6))<<endl;
return 0;
}



依据结果能够看出,无覆盖的情况下,派生类的虚函数直接放在第一张虚表后边。


5.单继承有覆盖

#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;} };
class Derive:public Base
{
public:
virtual void f(){cout<<"Derive::f1()"<<endl;}
virtual void g1(){cout<<"Derive::g1()"<<endl;}
virtual void h1(){cout<<"Derive::h1()"<<endl;} };
int main (void)
{
typedef unsigned long P_FUN;
typedef void(*FUN)(void); FUN f,g,h,f1,g1,h1;
Derive b; f1=(FUN)(*(P_FUN*)*(P_FUN*)&b);
g=(FUN)(*((P_FUN*)*(P_FUN*)&b+1));
h=(FUN)(*((P_FUN*)*(P_FUN*)&b+2));
g1=(FUN)(*((P_FUN*)*(P_FUN*)&b+3));
h1=(FUN)(*((P_FUN*)*(P_FUN*)&b+4)); f1();
g();
h();
g1();
h1(); cout<<(FUN)(*((P_FUN*)*(P_FUN*)&b+5))<<endl;
return 0;
}



依据执行结果能够看出,有覆盖的情况下,派生类虚函数覆盖到基类的虚函数,不覆盖的虚函数依次放在第一张表的后面。

6多继承有覆盖

#include<iostream>
using namespace std;
class Base1
{
private:
virtual void f(){cout<<"Base1::f()"<<endl;}
virtual void g(){cout<<"Base1::g()"<<endl;}
virtual void h(){cout<<"Base1::h()"<<endl;}
};
class Base2
{
public:
virtual void f(){cout<<"Base2::f()"<<endl;}
virtual void g(){cout<<"Base2::g()"<<endl;}
virtual void h(){cout<<"Base2::h()"<<endl;}
};
class Derive:public Base1,public Base2
{
public:
virtual void f(){cout<<"Derive::f1()"<<endl;}
virtual void g1(){cout<<"Derive::g1()"<<endl;}
virtual void h1(){cout<<"Derive::h1()"<<endl;}
};
int main (void)
{
typedef unsigned long P_FUN;
typedef void(*FUN)(void); FUN B1f,B1g,B1h,B2f,B2g,B2h,Dg,Dh;
Derive b; B1f=(FUN)(*(P_FUN*)*((P_FUN*)&b));
B1g=(FUN)(*((P_FUN*)*((P_FUN*)&b)+1));
B1h=(FUN)(*((P_FUN*)*((P_FUN*)&b)+2)); B2f=(FUN)(*(P_FUN*)*((P_FUN*)&b+1));
B2g=(FUN)(*((P_FUN*)*((P_FUN*)&b+1)+1));
B2h=(FUN)(*((P_FUN*)*((P_FUN*)&b+1)+2)); Dh=(FUN)(*((P_FUN*)*((P_FUN*)&b)+3));
Dg=(FUN)(*((P_FUN*)*((P_FUN*)&b)+4)); B1f();
B1g();
B1h();
cout<<endl; Dh();
Dg();
cout<<endl; B2f();
B2g();
B2h();
cout<<endl; cout<<(FUN)(*((P_FUN*)*((P_FUN*)&b)+5))<<endl;
cout<<(FUN)(*((P_FUN*)*((P_FUN*)&b+1)+3))<<endl; return 0;
}



依据结果可知,多继承下,覆盖的派生类函数会把全部的虚表中相相应的基类虚函数覆盖掉,不覆盖的派生类虚函数仅仅放在第一张虚表的后面。其它虚表后没有。


7.总结

1.当类的内部通过virtual关键字声明虚函数时,编译器生成vptr指针,指向虚函数表(虚表),虚表中依次存放着各虚函数(非虚函数不在当中)。

2.无覆盖的情况下。派生类的虚函数直接放在第一张虚表后边。

3.有覆盖的情况下。派生类虚函数覆盖到基类的虚函数,不覆盖的虚函数依次放在第一张表的后面。

4.多继承下,覆盖的派生类函数会把全部的虚表中相相应的基类虚函数覆盖掉,不覆盖的派生类虚函数仅仅放在第一张虚表的后面,其它虚表后没有。

PS:c++是一门威力十分强大的语言。内部构造复杂,或许有人说这些内容没什么卵用,但本人认为深入理解一下绝不是坏事,本文阅读的难点在于指针的应用,事实上整个c/c++的强大也是得益于指针,希望大家通过本文对c/c++有更深入的理解。

另外说明一下。指针确实是不安全的,比如本文中全部的public关键字删掉之后,通过指针仍然能够调用出类内的私有成员方法。

本人是一个即将大三的本科生,能力有限。博文如有不妥支出望各位读者指正。

c++中虚多态的实现机制的更多相关文章

  1. C++中虚函数功能的实现机制

    要理解C++中虚函数是如何工作的,需要回答四个问题. 1.  什么是虚函数. 虚函数由于必须是在类中声明的函数,因此又称为虚方法.所有以virtual修饰符开始的成员函数都成为虚方法.此时注意是vir ...

  2. 【高级】C++中虚函数机制的实现原理

    多态是C++中的一个重要特性,而虚函数却是实现多态的基石.所谓多态,就是基类的引用或者指针可以根据其实际指向的子类类型而表现出不同的功能.这篇文章讨论这种功能的实现原理,注意这里并不以某个具体的编译器 ...

  3. 第二十二篇:C++中的多态机制

    前言 封装性,继承性,多态性是面向对象语言的三大特性.其中封装,继承好理解,而多态的概念让许多初学者感到困惑.本文将讲述C++中多态的概念以及多态的实现机制. 什么是多态? 多态就是多种形态,就是许多 ...

  4. 简述C++中的多态机制

    前言 封装性,继承性,多态性是面向对象语言的三大特性.其中封装,继承好理解,而多态的概念让许多初学者感到困惑.本文将讲述C++中多态的概念以及多态的实现机制. 什么是多态? 多态就是多种形态,就是许多 ...

  5. 深入Java核心 Java中多态的实现机制(1)

    在疯狂java中,多态是这样解释的: 多态:相同类型的变量,调用同一个方法时,呈现出多中不同的行为特征, 这就是多态. 加上下面的解释:(多态四小类:强制的,重载的,参数的和包含的) 同时, 还用人这 ...

  6. java中实现多态的机制是什么?

    多态性是面向对象程序设计代码重用的一个重要机制,我们曾不只一次的提到Java多态性.在Java运行时多态性:继承和接口的实现一文中,我们曾详细介绍了Java实现运行时多态性的动态方法调度:今天我们再次 ...

  7. C++中的多态及虚函数大总结

    多态是C++中很关键的一部分,在面向对象程序设计中的作用尤为突出,其含义是具有多种形式或形态的情形,简单来说,多态:向不同对象发送同一个消息,不同的对象在接收时会产生不同的行为.即用一个函数名可以调用 ...

  8. c++中的多态机制

    目录 1  背景介绍 2  多态介绍 2-1  什么是多态 2-2  多态的分类 2-3  动态多态成立的条件 2-4  静态联编和动态联编 2-5  动态多态的实现原理    2-6   虚析构函数 ...

  9. 聊聊 C# 中的多态底层 (虚方法调用) 是怎么玩的

    最近在看 C++ 的虚方法调用实现原理,大概就是说在 class 的首位置存放着一个指向 vtable array 指针数组 的指针,而 vtable array 中的每一个指针元素指向的就是各自的 ...

随机推荐

  1. GoldenGate 传统抽取进程随 DataGuard 主备快速切换的方案(ADG 模式)

    环境描述: 1.节点描述 节点 IP 节点描述 11.6.76.221 GG 抽取端 / DG 节点,数据库版本号为 Oracle-11.2.0.3,与 11.6.76.222 组成 DataGuar ...

  2. 阿里云ecs遭到频繁的ddos攻击始末

    苦逼熬夜近俩月的时间搞出来个小东东,还指望它能给自己捞点~  结果刚上线没多久就遭到竞争对手疯狂的ddos攻击. 可怜的阿里云默认只能抗住5G的攻击,超出的直接黑洞,也是很无奈,然而能免费抗5G这在国 ...

  3. redis源码分析之事务Transaction(上)

    这周学习了一下redis事务功能的实现原理,本来是想用一篇文章进行总结的,写完以后发现这块内容比较多,而且多个命令之间又互相依赖,放在一篇文章里一方面篇幅会比较大,另一方面文章组织结构会比较乱,不容易 ...

  4. 无状态的web应用(单个py文件的Django占位图片服务器)

    本文为作者原创,转载请注明出处(http://www.cnblogs.com/mar-q/)by 负赑屃 阅读本文建议了解Django框架的基本工作流程,了解WSGI应用,如果对以上不是很清楚,建议结 ...

  5. jQuery+ajax实现局部刷新

    在项目中,经常会用到ajax,比如实现局部刷新,比如需要前后端交互等,这里呢分享局部刷新的两种方法,主要用的是ajax里面的.load(),其他高级方法的使用以后再做详细笔记. 第一种: 当某几个页面 ...

  6. 准备冲锋 golang入坑系列

    史前摘要: 本来想写读前必读,但连续几篇博文都写读前必读,感觉就没有了新意. 所以换成史前摘要,反正是一个意思. 此摘要的目的仍然是提醒点击而来的同学,本系列最新文章在这里.放到博客园的目的是为了方便 ...

  7. SpringAware

    哈哈,终于把分布式的课程演讲给混过去了,下面开始随便自己学点东西. 正题:SpringAware--------在实际项目中,用到spring容器的本省功能资源,这是Bean必须意识到Spring容器 ...

  8. 用一条SQL语句查出每门课都大于80分的学生的姓名

    用一条SQL语句查出每门课都大于80分的学生的姓名,数据表结构如下: 建表SQL如下: ; -- ---------------------------- -- Table structure for ...

  9. CentOS6.9编译安装Nginx1.12

    1:安装必要的库 Bash yum install gc gcc gcc-c++ pcre-devel zlib-devel openssl-devel 2:创建Nginx用户和组 Bash grou ...

  10. bootstrap的模态简单案例

    使用时需添加bootstrap的引用,否则实现不出来效果 <!DOCTYPE html> <html><head>    <meta name="v ...