美文网首页
深入理解 运行时多态: 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++

    1 运行时多态 基于 虚函数, 让 同一 Base ptr / ref ( 同一接口 ) 指向 不同 Deriv...

  • C++ 的多态(Polymorphism), virtual f

    多态 c++支持两种多态,编译时多态和运行时多态但其实编译时多态根本不是真正的多态,编译时多态就是函数的重载,ov...

  • C++ 运行时类型识别(RTTI)

    C++ 运行时类型识别(RTTI) C++以虚函数的形式支持了多态,某种形式上支持了运行时类型决议;但是dynam...

  • C++第六篇多态

    C++中的多态性分为编译时多态性和运行时多态性,编译时多态通过函数重载和模板体现,运行多态通过虚函数体现编译、连接...

  • 13.多态、虚函数、纯虚函数

    多态是对于不同对象接收相同消息时产生不同的动作。C++的多态性具体体现在运行时和编译时两个方面。运行时的多态是通过...

  • Java多态

    Java多态 编译时类型和运行时类型 理解编译时类型和运行时类型是理解多态的关键 上最直白的定义: Java的引用...

  • C++的多态

    C++三大特性:封装、继承和多态。其中最好理解的就是封装了,继承作为C++面向对象的特征也不难理解,那么多态,应该...

  • C++基础多态(PolyMorphism)

    C++按照实现的时机分为编译时多态和运行时多态1.编译时多态也成为静态连编,是指程序在编译的时候就确定了多态性,通...

  • 怎样理解Java的方法分派

    初级:多态,虚方法表的认识中级:对编译和运行时的理解和认识高级:对Java语言规范和运行机制的深入认识高级:横向对...

  • 多态

    1. 什么是运行时多态? 运行时多态或动态多态是运行时存在的多态。 如果方法被重写,则在运行时将调用哪个方法是未知...

网友评论

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

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