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. 如何实现border-width:0.5px;

    工作中遇到了一个产品需求,要求把列表分割线改成0.5px,直接写成border:0.5px solid #cccccc;是不符合规范的写法,会存在Android和IOS手机上的兼容问题,故,我们可以利 ...

  2. 利用C#实现分布式数据库查询

    随着传统的数据库.计算机网络和数字通信技术的飞速发展,以数据分布存储和分布处理为主要特征的分布式数据库系统的研究和开发越来越受到人们的关注.但由于其开发较为复杂,在一定程度上制约了它的发展.基于此,本 ...

  3. [转载] 快速理解Kafka分布式消息队列框架

    转载自http://blog.csdn.net/xiaolang85/article/details/18048631 ==是什么 == 简单的说,Kafka是由Linkedin开发的一个分布式的消息 ...

  4. 深度学习之tensorflow (一)

    一.TensorFlow简介 1.TensorFlow定义: tensor  :张量,N维数组 Flow   :  流,基于数据流图的计算 TensorFlow : 张量从图像的一端流动到另一端的计算 ...

  5. matlab R2016a 中添加新的工具箱的方法

    matlab添加新的工具箱分三步: 1. 下载新的工具箱,并解压. 2. 将解压后的工具箱文件夹移到安装的matlab中的toolbox文件夹中 3. 添加新文件夹及其子文件夹到路径中. 接下来以添加 ...

  6. Django 入门案例开发(中)

    昨天已经描述了如何搭建Django的开发环境,今天描述业务流程,具体我们要实现一个什么样的业务: 以下的业务都是假设的(网上书店   页面做的low): 1.用户注册及登录业务: 这是一个网上书店阅读 ...

  7. Android 之旅开始了!先自我了解下Android与Linux之间的关系

    Android是在Linux2.6的内核基础之上运行的,提供核心系统服务:安全.内存管理.进程管理.网络组.驱动模型.内核部分还相当于一个介于硬件层和系统中其他软件组之间的一个抽象层次.但是严格来说它 ...

  8. Spring集成Quartz完成定时任务

    在JavaEE系统中,我们经常会用到定时任务,比如每天晚上凌晨之后跑批处理或者是每天某个时刻群发消息等等. 我们可以使用java.util.Timer结合java.util.TimerTask来去完成 ...

  9. Redis Save 与 BGSAVE 的区别

    一,save保存数据到磁盘的方式: Redis Save 命令执行一个同步保存操作,将当前 Redis 实例的所有数据快照(snapshot)以 RDB 文件的形式保存到硬盘. 语法redis Sav ...

  10. js判断手机或Pc端登陆.并跳转到相应的页面

    <script src="~/Web/js/jquery-1.10.1.min.js"></script> <script> $(functio ...