zoukankan      html  css  js  c++  java
  • C++对象模型之虚函数实现原理

    在C++中,多态(polymorphism)的意思是,用基类的指针或者引用,寻址出一个派生类对象。而虚函数(virtual member function)是多态的基础,这也是面向对象编程迷人之处。现在刚好有时间,就写一下自己对C++在单一继承情况下如何实现虚函数的肤浅认识。

    一旦一个类有一个虚函数,那么一定会建立一个虚表(virtual table),虚表里面有所有虚函数的地址。虚表是该类所有对象共享的,每个对象都有一个虚指针(vptr),指向虚表。虚指针的设定与重置,是由类的构造函数与析构函数以及复制运算符(copy assignment)自动完成的。

    虚表是虚函数的基础。表格有很多槽(slot),通常第一个槽位存放的是每一个类所关联的类型信息(type_info object),用来支持运行时类型识别(RTTI)。

    class Demo{
    public:
    	Demo();
    	virtual ~Demo();
    	virtual void f1();
    	virtual void f2();
    	void f3();
    	static void f4();
    private:
    	static int ival;
    	long lval;
    };

    图1 Demo类的对象内存布局

    每个对象内部都会存储非静态成员变量以及虚指针,其它成员都会在对象外部存储,这些不是这次要讨论的内容。

    在C++中,虚函数在编译器间获知,而且这些虚函数的地址是固定不变的,执行期不可能新增或者被替换。为了找到函数的地址,每一个虚函数都会有一个在虚表里面的索引号。

    如果继承基类的过程中,派生类决定不改写某个虚函数,那么它就会继承基类这个函数的实体(定义),此时,在这个派生类的虚表中存放的就是基类的该函数。否则,如果派生类重写了基类的某个虚函数,那么派生类的虚表中存放的就会是对此虚函数重新定义的实体。如下图所示。

    class Base{
    public:
    	virtual void x();
    	virtual void y();
    	int ival;
    };
    //单一继承
    class Derived: public Base{
    public:
    	void x();
    	virtual void z();
    	long lval;
    };

    图2 单一继承情况下的虚表

    特别要注意的是:

    1)如果是继承基类声明的虚函数实体,那么该函数实体就会被拷贝到派生类虚表相对应的槽位上。

    2)如果是使用自己的函数实体,也就是改写基类的虚函数,那么也必须把这个函数实体地址放到相对应的槽位上。

    3)如果是新增加一个虚函数,那么虚表就会增加一个slot,并把新的虚函数实体放进这里。

    所以,假设对于上面的例子,我们有这样的用法:

    ptr->x();

    我们不知道ptr是Base还是Dervied指针,也就无法由ptr取得该对象的虚表,但是,我知道每一个x()函数的地址都会放在#1槽位上,所以,编译器会将该调用转换成

    (*ptr->vptr[1])(ptr);

    上面的表达式中,唯一需要在执行期才能知道的东西是,槽位1是哪个类虚表的,一旦知道了,那么便可调用相应的函数体了。

    就写到这。

    多重继承与虚拟继承下,虚函数的实现就没那么容易了。下次有空写一下。

  • 相关阅读:
    Namenode主节点停止报错 Error: flush failed for required journal
    IntelliJ IDEA2018.3 最新破解方法
    idea 中解决maven 包冲突的问题(maven helper)
    java中的守护线程
    synchronized锁住的是代码还是对象
    maven package,clean,install,compile命令
    设计模式——装饰者模式
    设计模式——观察者模式
    com.alibaba.fastjson.JSON对类对象的序列化与反序列化
    java8 Stream使用案例
  • 原文地址:https://www.cnblogs.com/xinyuyuanm/p/3073148.html
Copyright © 2011-2022 走看看