代码之家  ›  专栏  ›  技术社区  ›  pooya13

机器代码层上虚函数和条件执行的区别

  •  0
  • pooya13  · 技术社区  · 7 年前

    我试图理解虚函数的概念。根据 Wikipedia :

    简而言之,虚拟函数定义要执行的目标函数, 但目标可能在编译时未知。

    我的问题是,这与条件执行有何不同?

    void conditional_func(func_to_run) {
        switch(func_to_run) {
            case func1_tag: func1(); break;
            case func1_tag: func1(); break;
            ...
        }
    }
    
    int main() {
        read func_to_run
        conditional_func(func_to_run)
    }
    

    正如您所看到的,conditional\u func的最终目标在运行时是未知的。

    在C++中,虚拟函数似乎被定义为允许“多态性”的工具。我对多态性的定义: 多态类是具有对象的类,对象可以具有不同的形式(形态),而不是静态形式。也就是说,对象可以根据其子类具有不同的操作和属性。 (我在定义中避免提及特定语言的概念,如指针)

    因此,C++中所谓的虚拟函数甚至不必依赖于动态绑定(目标函数的运行时解析),而是可以在编译时具有已知的目标:

    int main()
    {
        Drived d;
        Base *bPtr = &d;
        bPtr->func();
    }
    

    在上面的示例中,编译器知道基指针指向派生对象,因此将知道func版本要运行的目标地址。因此,我的结论是,Wikipedia所指的虚拟函数与出于某种原因动态绑定的C++虚拟函数相同:

    int main()
    {
        Drived1 d1;
        Drived2 d2;
        read val;
        if (val == 1) Base *bPtr = &d1;
        else Base *bPtr = &d2;
        bPtr->func();
    }
    

    正如您所看到的,这也只是条件执行。下面是我的问题:

    1) 如果虚拟函数被定义为在编译时具有未知目标的函数,那么这与条件执行有何不同?它们在组装级别相同,但在更高的抽象层不同吗?

    2) 如果虚拟函数被定义为允许上述多态性的工具,那么这是否意味着它也只是更高级语言的一个概念?

    1 回复  |  直到 7 年前
        1
  •  1
  •   Tony Delroy    7 年前

    1) 如果虚拟函数被定义为在编译时具有未知目标的函数,那么这与条件执行有何不同?它们在组装级别相同,但在更高的抽象层不同吗?

    在程序集/机器代码级别,虚拟函数通常实现为特定于类的函数指针表(称为虚拟调度表或VDT),这些类型的每个对象都有一个指向其类表的指针。这些表的布局在基类和派生类之间是一致的,因此给定一个指向继承结构中任何对象的指针,任何给定虚拟函数的函数指针在所有类的VDT中始终处于相同的位置。这意味着相同的机器代码可以获取对象指针并找到要调用的函数。

    与您所演示的基于开关的代码类型的区别在于,所有带有此类开关的代码都需要手动更新和重新编译,以支持更多类型。使用函数指针,新类型的新代码可以简单地链接到通过指针工作的现有代码,而无需更改或重新编译后者。

    2) 如果虚拟函数被定义为允许上述多态性的工具,那么这是否意味着它也只是更高级语言的一个概念?

    首先,您试图定义多态性与C++术语不一致。您有:

    多态类是具有对象的类,对象可以具有不同的形式(形态),而不是静态形式。也就是说,对象可以根据其子类具有不同的操作和属性。

    在C++中,如果说任何给定的类都有一种形式,并且继承继承权中的不同类可能有不同的形式/操作/属性,那么这更接近事实。

    向前在机器代码级别,您可以使用函数指针,并获得与虚拟函数相同的运行时效果,这显然是因为C++必须输出机器代码。

    虚拟函数增加的是让编译器为您完成大部分工作的方便性和可靠性:

    • 创建虚拟调度表,
    • 确保函数指针的顺序一致,
    • 给对象一个指向这些表的隐式指针,并在第一个非抽象基的构造函数中可靠地对其进行初始化,并在构造时将其更新为实际对象的运行时类型的类继承权,然后在析构函数启动时反转指针的值,
    • 检查重写是否具有与其重写的虚拟函数相同的函数签名,
    • 当调用的函数可以在编译时推导出来时,可以选择优化运行时调度。

    这种保证和编译器生成的操作使得C++风格的虚拟函数和调度比程序员协调使用函数指针更高级的语言特性,更不用说切换运行时类型了。也就是说,没有什么特别能阻止某人向汇编语言添加这样的支持。也就是说,许多比C++更高级的语言缺少类似于虚拟函数的东西。(在极端情况下 5GL 甚至可能不会向“程序员”/用户公开函数的概念)。