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. [MYSQL] 记一次MySQL性能调优

    最近在做数据迁移工作,已有一堆数据文件,要把这些数据文件写到MySQL 数据库里面去. MySQL数据库上架了一层服务接口,可以直接调用.博主写了一个迁移程序,放在服务器A上. *********** ...

  2. 11) 十分钟学会android--Intent消息处理与传递详解

    一个Android app通常都会有多个activities. 每个activity的界面都扮演者用户接口的角色,允许用户执行一些特定任务(例如查看地图或者是开始拍照等).为了让用户能够从一个acti ...

  3. Erlang Concurrent 并发进阶

    写在前面的话 本文来源于官方教程 Erlang -- Concurrent Programming.虽然没有逻辑上的关系,但建议在掌握了Erlang入门系列教程的一些前置知识后继续阅读. 之前我是逐小 ...

  4. Docker安装和卸载

    一:卸载旧版本 老版本的Docker被称为docker或docker-engine.如果安装了这些,请卸载它们以及相关的依赖项. $ sudo yum remove docker \ docker-c ...

  5. MyEclipse下安装FatJar打包工具

    方法一:help > software updates > Find and install > add > add remote site name:FatJar url:h ...

  6. js 切换全屏

    公司有一个需求就是点击某一个按钮实现全屏切换功能,然后呢我就在网上扒了段代码.封装了一下.使用的小伙伴们可以看看哦! 切换全屏代码 <!DOCTYPE html> <html> ...

  7. SQL SERVER 2012 SEQUENCE

    一.Sequence简介 Sequence对象对于Oracle用户来说是最熟悉不过的数据库对象了, 在SQL SERVER2012终于也可以看到这个对象了.Sequence是SQL Server201 ...

  8. 华南师大 2017 年 ACM 程序设计竞赛新生初赛题解

    题解 被你们虐了千百遍的题目和 OJ 也很累的,也想要休息,所以你们别想了,行行好放过它们,我们来看题解吧... A. 诡异的计数法 Description cgy 太喜欢质数了以至于他计数也需要用质 ...

  9. android 圆角 ImageView

    android中Imageview 内的图片圆角的实现方式 此文针对的是 imageview中图片的圆角, 即忽略掉图片的ScaleType, 均对图片采取圆角.  而不是对Imageview本身的圆 ...

  10. 初识CC_MVPMatrix

    初识CC_MVPMatrix CC_MVPMatrix是一个mat4类型的uniform,在shader代码被编译之前,它由cocos2d-x框架插入进来的. bool GLProgram::comp ...