虚函数表

摘要:
此外,父对象和子对象都有vptr指针。传入不同的对象。编译器将根据vptr指针在自己的虚拟函数表中找到自己的函数。这里,关于虚拟函数表有两点需要解释:注1:当程序运行时,通过虚拟函数表指针VPTR调用函数重写。因此,需要寻址操作来确定应该调用的函数。就效率而言,虚拟功能的效率要低得多。虚拟函数表本质上是一个指针数组,其中存储了虚拟函数的函数指针。

(https://www.cnblogs.com/vipchenwei/p/7466018.html?utm_source=debugrun&utm_medium=referral)

一、

VPTR指针:虚指针(含有虚函数的类对象中都有一个需指针)

虚函数表:含有虚函数的类对象中都有一个虚函数表

  这里我们主要来探究一下,编译器在什么地方动了手脚,从而支持了多态?

  从一段代码来分析:

  下面代码,我把C++编译器可能动手脚地方标注出来,看看编译器到底是在什么时候就实现不同对象能调用同名函数绑定关系。

class Parent{
    public:
        Parent(int a=0){
            this->a = a;}
        virtual void print(){ //地方1  
            cout<<"parent"<<endl;}  
    private:
        int a;
};

class Son:public Parent{
    public:
       Son(int a=0,int b=0):Parent(a){
           this->b = b;}
       void print(){
           cout<<"Son"<<endl;}
    private:
        int b;
    };

  void play(Parent *p){ //地方2  
        p->print();}

    void main(int argc, char const *argv[])
    {
        Parent p; //地方3  
        Son s;
        play(&s)
        return 0;
    }

 在地方1处,我们知道,既然函数被声明了virtual,编译器会做特殊处理,会不会是这里确定了绑定关系呢?

  在地方2处,我们知道,当传来子类对象, 执行子类函数, 传来父类对象,执行父类对象,多态就是在这里发生的,那会不会在这里呢?

  但是,恰恰我们最容易忽视就是在地方3处,真正确定绑定关系的地方,就是创建对象的时候!!这时候C++编译器会偷偷的给对象添加一个vptr指针。

  只要我们在类中定义了virtual函数,那么我们在定义对象的时候,C++编译器会在对象中存储一个vptr指针,类中创建的虚函数的地址会存放在虚函数表中vptr指针就是指向这个表的首地址

虚函数表第1张

 

 

在发生多态的地方,编译器根本不会去区分,传进来的是子类对象还是父类对象。而是关心print()是否为虚函数,

如果是虚函数,就根据不同对象的vptr指针找属于自己的函数。

而且父类对象和子类对象都有vptr指针,传入对象不同,编译器会根据vptr指针,到属于自己虚函数表中找自己的函数。即:vptr--->虚函数表------>函数的入口地址,从而实现了迟绑定(在运行的时候,才会去判断)。

  如果不是虚函数,那么这种绑定关系在编译的时候就已经确定的,也就是静态联编!

  这里,关于虚函数表要说明两点:

  说明1:通过虚函数表指针VPTR调用重写函数是在程序运行时进行的,(运行时多态)

因此需要通过寻址操作才能确定真正应该调用的函数。而普通成员函数是在编译时就确定了调用的函数。在效率上,虚函数的效率要低很多。

  说明2:出于效率考虑,没有必要将所有成员函数都声明为虚函数

  说明3 :C++编译器,执行非虚函数,不需要区分是子类对象还是父类对象

  最后,我们来总结一下多态的实现原理:   

  • 当类中声明虚函数时,编译器会在类中生成一个虚函数表
  • 虚函数表是一个存储类成员函数指针的数据结构
  • 虚函数表是由编译器自动生成与维护的,virtual成员函数会被编译器放入虚函数表中

 

二、

问题:构造函数中能调用虚函数,实现多态么?

  等价于:对象中的vptr指针什么时候初始化?

class Parent{
public:
    Parent(int a=0){
            this->a = a;
            print();}
    virtual void print(){cout<<"Parent"<<endl;}
private:
    int a;
};
class Son:public Parent{
    Son(int a=0,int b=0):Parent(a){
        this->b = b;
        print();}
    virtual void print(){cout<<"Son"<<endl;}
};
void main(int argc, char const *argv[]){
        Son s;
        return 0;
}

当我们定义对象的时候,会执行构造函数,但是,在构造函数里面,我们调用了虚函数print(),那么这里的print()会执行哪个?会发生多态么??

  测试发现:两个类中构造函数中,都只会调用自己类中的print()函数。

  为什么会这样?为什么没有发生多态?(注:子类对象在构造时,是从基类开始,先调用基类的构造函数,再调用子类的构造函数)

虚函数表第2张

 

https://www.cnblogs.com/hushpa/p/5707475.html

1. 基础知识:
(1) 32位os 指针长度为4字节, 64位os 指针长度为8字节, 下面的分析环境为64位 linux & g++ 4.8.4.
(2) new一个对象时, 只为类中成员变量分配空间, 对象之间共享成员函数(通过this参数,区分不同的对象)。

2 虚函数表
    包含虚函数的类才会有虚函数表, 同属于一个类的对象共享虚函数表, 但是有各自的_vptr.

_vptr存放在对象内存的开始, vs和g++都是。


    虚函数表实质是一个指针数组,里面存的是虚函数的函数指针。

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:
    void g() {cout<<"derive::g"<<endl;}
};

虚函数表第3张

 5、多继承

虚函数表第4张

免责声明:文章转载自《虚函数表》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Opendaylight的Carbon(碳)版本安装Android 播放Gif 动画下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Android JNI和NDK学习(08)JNI实例一 传递基本类型数据

Android JNI和NDK学习(08)--JNI实例一 传递基本类型数据 本文介绍在Java和JNI之间相互传递基本数据类型的方法。 由于前面已经详细介绍搭建和建立NDK工程的完整流程(参考“静态实现流程”或“动态实现流程”),这里就不再介绍流程;而是将重点放在说明如何实现Java和JNI之间相互传递基本数据。 1 建立eclipse工程 建立工程Nd...

Linux内核数据结构hlist_head

参考自:https://blog.csdn.net/zhanglei4214/article/details/6767288 一、hlist结构简介 hlist_head 和 hlist_node 是位于linux内核中的数据结构,其设计初衷主要是为了减少Hash表的内存消耗。 structhlist_head { struct hlist_node...

dev -c++ 快捷键

转自:http://blog.csdn.net/abcjennifer/article/details/7259222 F8:开始调试 F7:进一步执行当前行,并跳到下一行 F4:添加查看 ctrl + F7 跳到下一断点, shift + F4 跳到光标所在行,并在该行设置断点 用鼠标选择源文件中的变量名,然后按 F4 也可以查看变量的值,该变量会出现...

windows编程--x64调用约定

windows32位程序包括stdcall,thiscall,fastcall,cdecl,clrcall,vectorcall,nakedcall等调用方式,x64位程序默认使用新的fastcall调用方式。 这种调用方式得益于x64平台寄存器数量的增加。  x64 fastcall调用约定 空间大于8字节的参数用参照传递,不能把一个参数分割到多个寄存器...

鼠标指针下总出现一个滚动条似的方框,怎么办?有图

鼠标指针下总出现一个滚动条似的方框,怎么办?有图 本人的解决办法: 控制面板 -》硬件声音-》鼠标-》指针-》去掉最下面允许主题更改指针(L)的复选框-》点击确定。   ps:也可以从桌面-》右键-》个性化-》更改鼠标指针-》指针 进去设置...

【学习总结】《大话数据结构》- 第6章-树

【学习总结】《大话数据结构》- 总 第6章树-代码链接 启示: 树 目录 6.1 开场白 6.2 树的定义 6.3 树的抽象数据类型 6.4 树的存储结构 6.5 二叉树的定义 6.6 二叉树的性质 6.7 二叉树的存储结构 6.8 遍历二叉树 6.9 二叉树的建立 6.10 线索二叉树 6.11 树、森林与二叉树的转换 6.12 赫夫曼树及其应用 6...