美文网首页
深入理解 运行时多态: C++

深入理解 运行时多态: C++

作者: my_passion | 来源:发表于2020-05-30 14:41 被阅读0次

    1 运行时多态

    / 动态多态 / 动态绑定
    

    基于 虚函数, 让 同一 Base ptr / ref ( 同一接口 ) 指向 不同 Derived obj ( 不同 对象 ), 以 呈现出 不同 形态 ( 行为 )

    编译时多态 / 静态多态 / 静态绑定:
    
    基于 `non-vf`, 按 non-vf ptr 去调 
    `不同 重载版本 or 模板实例化`
    
    `静态多态 (实际上 不算多态) 的 2 种形式:`
    
    `1) 函数重载`
    `2) 模板`
    
    比较:
    
    1) compiler `编译时 就要确定` 函数的 
    `参数类型 (只能确定1个类型)`,
    运行时 直接调 函数
    
    2) 用 `virtual 关键字 告诉 compiler`, 
    `编译时 先别确定  参数类型`, 
    运行时 再根据 `传入的 实际参数类型` 确定
    

    1) 编译时 ptr / ref 形参 type (Base*) 调 所属 class (Base) non-vf

    2) 运行时 ptr / ref 形参 所指 实际对象 typevf

    形参 pbase 的 `静/动 态类型` 是 `Base / Derived`
    

    1.1 non-vf + 静态绑定

    non-virtual ( mem ) func 与 non-mem func 的 区别: 
    
    仅是 `第 1 para 是 this 指针`
    
    `compiler 在`
    

    (1) 编译 时, 据 class definition 分配 func ptr

    (2) 调 non-vf 时 ( 运行时 ), 据 this 指针 编译时 type, 取 相应 func ptr, 调用之

    1.2 vf + 动态绑定

    1) 只能 用 Base `ptr/ ref 实现`
    
    2) 不能 用 object 实现
    reason: obj 的 type 固定, 无法实现 动态类型
    
    `compiler 在 `
    

    (1) 编译时

    1) 对 每个 class, 创建 其 vtbl(s):

    据 class definition 分配 vf ptr ( addr ), 写入 相应 vtbl固定 index 位置

    2) 将 vf 调用 pb->vf() 转化( * (pb->vptr_i)[index] ) (pb)

    vf ptr 写入 vtbl 中 哪个 index 位置 ?

    答: 普遍实现
    
    1> 继承的 vf 优先
    
    继承的 vf  `无论是否被 override`, 按 
    `Base 中 vf 声明顺序`
    
    只是 vf ptr 指向 Derived / Base 相应 vf
    
    2> 本 class `新增 vf`, 按 
    `Derived 中 vf 声明顺序`
    
    `index = 0 处 写 type_info`
    `从 index = 1 开始, 依次写 相应 vf ptr`
    

    (2) 构造 obj 时

    `运行时`
    

    obj 内存 起始处 写 vptr ( vptr_i ): 指向 obj 实际 class type 的 vtbl

    (3) 调 pb->vf() 时

    `运行时`
    

    pb 指向 哪种 type ( Base / Derived ) obj, 就从该 type vtbl, 即 pb->vptr_i 所指 vtbl取出 相应 vf_ptr: (pb->vptr_i)[index], 并 调用之: ( * (pb->vptr_i)[index] ) (pb) = ( * (this->vptr_i)[index] ) (this)

    pb 作 mem func 第1实参 隐含传给 第1形参 this

    `多继承 有 多 个 vptr ( vptr_i, i = 1, 2, ...)`
    

    1.3 C++ 调 vf 如何确定 从 哪个 vtbl / index 取 func ptr?

    << 深度探索C++对象模型 >> Function 章 
    

    (1) 从 哪个 vtbl 取 ?

    `vf 的 调用` 在 `编译期 被转化为 ( * (p->vptr)[index] )(p)`  的形式
    
    => 
    
    `并不是 写死了 调哪个 vf, 只是告诉 compiler`, 
    

    p->vptr 所指 vtbl ( 运行时 才知是 哪个 vtbl ) 中 index ( 如 index == 1 ) 位置 取 vf ptr

    `编译期 无法确定 从 哪个 vtbl 去 取`, 
    因为 `Base ptr 可能 指向 Base / Derived obj`;
    
    `运行期` 才能 据 `Base ptr 所指 obj`, 
    `从 该 obj 中 p->vptr 所指 vtbl 中 去取`
    
    `(2) 从 哪个 index 取 ?` 
    `compiler 编译时 指定 vf ptr 写入 相应 vtbl 哪个 index, 
    调用 时 就从该 index 取 `
    
    class Base
    {
    public:
        virtual void func1() {}
        virtual void func2() {}
    };
    
    class Derived : public Base
    {
    public:
        // override
        virtual void func1() {}
    
        virtual void func3() {}
    };
    
    int main()
    {
        Base* pb = new Derived;
        pb->func1();
    }
    
    // single inheritance:
    Base vtbl:
    index = 1: &Base::func1
    index = 2: &Base::func2
    
    Derived vtbl:
    index = 1: &Derived::func1 // inherit + override
    index = 2: &Base::func2    // inherit + not override
    index = 3: &Derived::func3
    

    1.4 note: 用 Base ptr / ref 实现 动态多态前提

    Base 必须 有 相应 vf; 否则, 调的是 Base non-vf, 或 调用失败

    => 无法实现 动态多态
    
    见 3.1
    

    2 实现 角度:vf + Inheritance + vptr + vtbl

    vtbl : virtual table
    vptr : virtual talbe pointer
    vf   : virtual function
    

    类 继承 or 声明 了 vf, 就有了自己的 vtbls ( n 继承 有 n个 vtbl )

    2.1 单继承

    2.jpg 3.jpg

    2.2 多继承

    vptrn: 指向 `继承的 第 n 个 Base` 的 vtbl
    
    `1 种实现:`
    

    vtbl 中 vf ptr 顺序: 按 vf 声明顺序

    `vptr1  与 vptr2-n / vtbl 与 vtbl2-n 的 异同?`
    
    `vptr1 / vptr2-n 指向 vtbl / vtbl2-n`
    
    同:
    `override / 未 override` 时, 
    `vtbl 中 vf ptr 指向 Derived / Base 中 vf`
    
    异:
    

    vtbl1 中 vf ptr 还要指向 Derived 新增 vf

    而 vtbl2-n 中 vf pointer 则 不用...
    
    4.jpg

    3 class 内存分布 角度:单 / 多 / 菱形 继承

    3.1 单继承

    eg: `Base + non-vf1 / Derived2nd + vf1 =>
    Base ptr 调 Derived2nd 对象的 f1, 
    调的是 Base 的 non-vf1`
    
    5.jpg
    #include<iostream>
    class Base 
    {
    public:
        char *dataBase;
        void f1() {}
        virtual void f2() {}
    };
    
    class Derived: public Base 
    {
    public:
        int dataDerived;
        virtual void f1() { } // not override f1 in base
        void f2()         { }  // virtual from Inheritance : override f2 in Base
    };
    
    class Derived2nd : public Derived 
    {
    public:
        int dataDerived2nd;
        void f1() { }   // virtual from Inheritance : override f1 in Derived
        void f2() { }  //  virtual from Inheritance : override f2 in Derived and Base
    };
    
    int main(void) 
    {
        Base base;
        Derived derived;
        Derived2nd derived2nd;
    
        Base *pbase = NULL;
        Derived *pderived = NULL;
        Derived2nd *pderived2nd = NULL;
    
        pbase = &derived;
        pbase->f1(); // Base1 f1
        pbase->f2(); // Derived f2
    
        pbase = &derived2nd;
        pbase->f1(); // Base f1
        pbase->f2(); // Derived2nd f2
    
        pderived = &derived2nd;
        pderived->f1(); // Derived2nd f1
        pderived->f2(); // Derived2nd f2
    }
    

    类的 内存分布

    VS 工程 -> 属性 -> C/C++ -> 命令行 -> 其他选项 
    -> 填 /d1 reportAllClassLayout -> 应用 -> 确定
    
    image.png
    编译后, 输出窗口
    
    image.png
    class Base  size(8):
    +---
    0   | {vfptr}
    4   | dataBase
    +---
    
    Base::$vftable@:
    | &Base_meta
    |  0
    0   | &Base::f2
    
    
    class Derived   size(12):
    +---
    | +--- (base class Base)
    0   | | {vfptr}
    4   | | dataBase
    | +---
    8   | dataDerived
    +---
    
    Derived::$vftable@:
    | &Derived_meta
    |  0
    0   | &Derived::f2
    1   | &Derived::f1
    
    class Derived2nd    size(16):
    +---
    | +--- (base class Derived)
    | | +--- (base class Base)
    0   | | | {vfptr}
    4   | | | dataBase
    | | +---
    8   | | dataDerived
    | +---
    12  | dataDerived2nd
    +---
    
    Derived2nd::$vftable@:
    | &Derived2nd_meta
    |  0
    0   | &Derived2nd::f2
    1   | &Derived2nd::f1
    
    

    3.2 多继承

    子类 含多个 基类的 内存结构, 包括多个 vtbl
    
    每个继承 当单继承分析
    
    6.jpg
    #include<iostream>
    class Base1 {
    public:
        int dataBase1;
        virtual void f1() { }
    };
    
    class Base2 {
    public:
        int dataBase2;
        virtual void f2() { }
    };
    
    class Derived: public Base1, public Base2 {
    public:
        int dataDerived;
        void f1() {}
        void f2() {}
        void f3() {}
    };
    
    int main(void) {
        Derived derived;
        Base1 *pbase1 = NULL;
        Base2 *pbase2 = NULL;
    
        pbase1 = &derived;
        pbase1->f1(); //Derived f1
    
        pbase2 = &derived;
        pbase2->f2(); //Derived f2
    }
    
    class Base1 size(8):
    +---
    0   | {vfptr}
    4   | dataBase1
    +---
    
    Base1::$vftable@:
    | &Base1_meta
    |  0
    0   | &Base1::f1
    
    class Base2 size(8):
    +---
    0   | {vfptr}
    4   | dataBase2
    +---
    
    Base2::$vftable@:
    | &Base2_meta
    |  0
    0   | &Base2::f2
    
    
    class Derived   size(20):
    +---
    | +--- (base class Base1)
    0   | | {vfptr}
    4   | | dataBase1
    | +---
    | +--- (base class Base2)
    8   | | {vfptr}
    12  | | dataBase2
    | +---
    16  | dataDerived
    +---
    
    Derived::$vftable@Base1@:
    | &Derived_meta
    |  0
    0   | &Derived::f1
    
    Derived::$vftable@Base2@:
    | -8                        //why is -8?
    0   | &Derived::f2
    

    3.3 菱形继承

    1. 1级继承 not 虚继承

    7.jpg

    菱形冲突:
    (1) 最远子类 从 2个 中间子类 都继承了 共同父类共同 mem data/func => 最远子类对象 中 存了2份 Inherited 共同父类 的 mem data / vptr

    (2) 用 共同父类 pointer最远子类对象 该 共同 mem data/func 时, compiler 不知道该 调2份中哪1份 => 编译报错 base class is ambiguous

    解决: 中间子类均 
    

    虚继承

    共同父类, 
    

    每个 中间子类 增加1个 vbptr, 指向 共同虚基类

    `vbptr: virtual base table pointer`
    
    #include<iostream>
    class Base {
    public:
        int dateBase;
        virtual void f1(){}
    };
    
    class Derived1: public Base {
    public:
        int dataDerived1;
        virtual void f2(){}
    };
    
    class Derived2: public Base {
    public:
        int dataDerived2;
        virtual void f3(){}
    };
    
    class Derived2nd: public Derived2, public Derived1 {
    public:
        int dateDerived2nd;
        void f1() {}
        void f2() {}
        void f3() {} 
    };
    
    int main(void) {
        Derived2nd derived2nd;
    
        Derived1 *pderived1 = NULL;
        pderived1 = &derived2nd;
        pderived1->f2();
    
        Base *pbase = NULL;
        pbase = &derived2nd; // error: base class is ambiguous 
    }
    
    
    class Base  size(8):
    +---
    0   | {vfptr}
    4   | dateBase
    +---
    
    Base::$vftable@:
    | &Base_meta
    |  0
    0   | &Base::f1
    
    
    class Derived1  size(12):
    +---
    | +--- (base class Base)
    0   | | {vfptr}
    4   | | dateBase
    | +---
    8   | dataDerived1
    +---
    
    Derived1::$vftable@:
    | &Derived1_meta
    |  0
    0   | &Base::f1
    1   | &Derived1::f2
    
    
    class Derived2  size(12):
    +---
    | +--- (base class Base)
    0   | | {vfptr}
    4   | | dateBase
    | +---
    8   | dataDerived2
    +---
    
    Derived2::$vftable@:
    | &Derived2_meta
    |  0
    0   | &Base::f1
    1   | &Derived2::f3
    
    
    class Derived2nd    size(28):
    +---
    | +--- (base class Derived2)
    | | +--- (base class Base)
    0   | | | {vfptr}
    4   | | | dateBase
    | | +---
    8   | | dataDerived2
    | +---
    | +--- (base class Derived1)
    | | +--- (base class Base)
    12  | | | {vfptr}
    16  | | | dateBase
    | | +---
    20  | | dataDerived1
    | +---
    24  | dateDerived2nd
    +---
    
    Derived2nd::$vftable@Derived2@:
    | &Derived2nd_meta
    |  0
    0   | &Derived2nd::f1
    1   | &Derived2nd::f3
    
    Derived2nd::$vftable@Derived1@:
    | -12
    0   | &thunk: this-=12; goto Derived2nd::f1
    1   | &Derived2nd::f2
    

    2. 虚继承

    8.jpg
    #include<iostream>
    using namespace std;
    
    class Base {
    public:
        int dateBase;
        virtual void f1() {}
    };
    
    // virtual Inherit
    class Derived1: virtual public Base { 
    public:
        int dataDerived1;
        virtual void f2() {}
    };
    
    // virtual Inherit
    class Derived2: virtual public Base {  
    public:
        int dataDerived2;
        virtual void f3(){}
    };
    
    class Derived2nd: public Derived2, public Derived1 {
    public:
        int dateDerived2nd;
        void f1() {}
        void f2() {}
        void f3() {}
    };
    
    int main(void) 
    {
        Derived2nd derived2nd;
    
        Derived1 *pderived1 = NULL;
        pderived1 = &derived2nd;
        pderived1->f2();
    
        Base *pbase = NULL;
        pbase = &derived2nd; 
        pbase->f1();
    }
    
    class Base  size(8):
    +---
    0   | {vfptr}
    4   | dateBase
    +---
    
    Base::$vftable@:
    | &Base_meta
    |  0
    0   | &Base::f1
    
    Base::f1 this adjustor: 0
    
    -----------------------------
    class Derived1  size(20):
    +---
    0   | {vfptr}
    4   | {vbptr}
    8   | dataDerived1
    +---
    +--- (virtual base Base)
    12  | {vfptr}
    16  | dateBase
    +---
    
    Derived1::$vftable@Derived1@:
    | &Derived1_meta
    |  0
    0   | &Derived1::f2
    
    Derived1::$vbtable@:
    0   | -4
    1   | 8 (Derived1d(Derived1+4)Base)
    
    Derived1::$vftable@Base@:
    | -12
    0   | &Base::f1
    
    Derived1::f2 this adjustor: 0
    
    vbi:  class  offset o.vbptr  o.vbte fVtorDisp
          Base      12       4       4   0
    
    
    -----------------------------
    
    class Derived2  size(20):
    +---
    0   | {vfptr}
    4   | {vbptr}
    8   | dataDerived2
    +---
    +--- (virtual base Base)
    12  | {vfptr}
    16  | dateBase
    +---
    
    Derived2::$vftable@Derived2@:
    | &Derived2_meta
    |  0
    0   | &Derived2::f3
    
    Derived2::$vbtable@:
    0   | -4
    1   | 8 (Derived2d(Derived2+4)Base)
    
    Derived2::$vftable@Base@:
    | -12
    0   | &Base::f1
    
    Derived2::f3 this adjustor: 0
    
    vbi: class  offset o.vbptr  o.vbte fVtorDisp
         Base      12       4       4   0
    
    -----------------------------
    class Derived2nd    size(36):
    +---
    | +--- (base class Derived2)
    0   | | {vfptr}
    4   | | {vbptr}
    8   | | dataDerived2
    | +---
    | +--- (base class Derived1)
    12  | | {vfptr}
    16  | | {vbptr}
    20  | | dataDerived1
    | +---
    24  | dateDerived2nd
    +---
    +--- (virtual base Base)
    28  | {vfptr}
    32  | dateBase
    +---
    
    Derived2nd::$vftable@Derived2@:
    | &Derived2nd_meta
    |  0
    0   | &Derived2nd::f3
    
    Derived2nd::$vftable@Derived1@:
    | -12
    0   | &Derived2nd::f2
    
    Derived2nd::$vbtable@Derived2@:
    0   | -4
    1   | 24 (Derived2ndd(Derived2+4)Base)
    
    Derived2nd::$vbtable@Derived1@:
    0   | -4
    1   | 12 (Derived2ndd(Derived1+4)Base)
    
    Derived2nd::$vftable@Base@:
    | -28
    0   | &Derived2nd::f1
    
    Derived2nd::f1 this adjustor: 28
    Derived2nd::f2 this adjustor: 12
    Derived2nd::f3 this adjustor: 0
    

    4 Covariant / 协变 return type

    1. 解决的问题

    overridden vf 在 Base / Derivedreturn type 通常 相同

    `为支持 overridden vf 在 Base / Derived 中` 
    

    return type 为 父 type / 子 type => 以 省去 不必要的 type conversion

    引入 `Covariant Return Types`
    
    2. 应用
    
    // overridden vf 在 Derived 中 return type == Base 中 return type
    virtual Base * 
    clone() const override 
    {
        return new Derived(*this); 
    }
    
    非 Covariant return type 时,
    

    必须 用 pb + dynamic_cast<Derived *>

    Derived* pd = new Derived();
    Base* pb = pd->clone();
    Derived *pd2 = dynamic_cast<Derived *>(pb);
    
    引入 Covariant return type,
    

    省去 不必要的 pb + dynamic_cast

    // overridden vf 在 Derived 中 return type 
    // 是 Base 中 return type 的 子类
    virtual Derived * 
    clone() const override 
    {
        return new Derived(*this); 
    }
    
    Derived *pd1 = new Derived();
    Derived *pd2 = pd1->clone();
    
    class Base 
    {
    public:
        virtual Base * clone() const 
        {
            return new Base(*this); 
        }
    };
    
    `3. C++_Idioms`
    

    https://en.m.wikibooks.org/wiki/More_C++_Idioms/Covariant_Return_Types

    5 虚 dtor

    1. Base dtor 非虚 的 问题

    `销毁 obj 时, 不自动调用 其 所属 class 的 dtor 的 特例`
    
    `1) Base ptr` 指向 
    

    newed Derived obj

    `2) Derived 内 mem ptr` 指向 
    

    dynamic memory

    3) Base dtor non-virtual

    => 
    

    delete Base ptr ( => destory Derived obj ) 时, 调的 dtor编译时绑定的 Bsae dtor

    =>
    
    `Derived 内 mem ptr` 所指 
    

    dynamic memory 未被释放

    1.jpg

    2. Base dtor 非虚 / 虚 : pbase->dtorpbase 编译/运行 时 相应 type (Base / Derived) 的 dtor

    class Base{
    public:
        ~Base() { }
    };
    
    class Derived : public Base{
    public:
        Derived();
        ~Derived();
    private:
        int *ptr;
    };
    
    Derived::Derived() {
        ptr = new int(0);
    }
    
    Derived::~Derived(){
        delete ptr;
    }
    
    viod fun(Base* pbase){
        delete pbase;
    }
    
    int main()
    {
        Base* pbase = new Derived();
        fun(pbase);
    }
    

    6 两个函数间 overload override hide

    1. func 同 原型

    `同`
    `1) 名`
    `2) para` 
        `type / 个数 / 顺序 有不同, 则 不同 para`
    `3) return type`
    `4) const  / virtual`
    
    `2. 继承 virtual (function) 属性 的 2种情形`
    
    `除 virtual` keyword 外, 若 `同 原型 / 协变 return type`
    

    3. function 间 hide / overload / override

    1) hide: 同名

    , 不 care para/return type 等`
    

    2) overload: 不同 para

    `不 care return type`
    

    compiler 对 overload func name 粉碎

    3) override: 父 子 类 + vf ( 同 原型 / 协变 return type )

    override 关键字 可用于 explicitly declare 函数覆盖
    

    final + class / mem func: 不允许被 继承 / override

    7 运行时 多态 的 条件

    1) 子类 override 父类 vf ( 同 原型 / 协变 return type)

    2) Base ptr / ref 调 Derived vf

    8 virtual 与 static / friend / inline / dtor / ctor

    virtual:

    开启 动态绑定, 据 
    

    对象动态类型 选择调 vf

    (1) static mem func 属于类 而不是 对象

    => + virutal 报错
    
    `static mem func 没 this 指针` 
    => &obj 无法自动传入
    => 无法体现 多态
    

    (2) friend 函数 不支持 继承 => 不能声明 为 vf

    + virtual = 报错
    

    (3) inline mem func: 编译时 绑定

    + virtual ( vf 运行时绑定 ) = 报错
    

    (4) class 作 Base, dtor 要为 virtual

    否则, 
    pb->dtor 调的是 Base dtor 
    => Derived 内 dynamic memory 未被释放
    

    (5) ctor 不能为 virtual

    Ctor 中 某个点 
                
        对象的 (动态) 类型 `仅反映 当前已构造完成部分`
                    
            类层次 中 `某个 类 虚 Ctor`
                        
                会调用 `本类 或 更上层类` 中的 `Ctor 版本`           
                                 |
                                 |/
                                error   
    

    对象 -> vptr -> vtbl -> 虚 ctor -> 构造出对象 => 矛盾

    相关文章

      网友评论

          本文标题:深入理解 运行时多态: C++

          本文链接:https://www.haomeiwen.com/subject/pyoczhtx.html