繁体   English   中英

为什么在运行时处理虚函数?

[英]Why are virtual functions handled at runtime?

当然,编译器足够智能,可以在某些情况下精确推断出您想要的功能,但其他情况如何需要运行时支持呢?

因为我们并不总是知道,我们将在运行时面对什么实例。

例如,您有类: SuperClassSubclass1Subclass2 ,它们都有一个方法doACoolThing() 在用户按下按钮012 ,并且根据他的输入,创建相应的类的一个实例,其doACoolThing()方法被调用。

我们(以及编译器)也无法确定在运行时调用哪个类的方法。

这就是为什么这些技巧需要运行时支持。

一个小例子来说明一个想法(PS不写这样的代码,它只是为了说明多态性:)):

#include <iostream>

using namespace std;

class SuperClass
{
public:
    virtual void doACoolThing();
};

void SuperClass::doACoolThing()
{
    cout << "Hello from the SuperClass!" << endl;
}

class Subclass1 : public SuperClass
{
    virtual void doACoolThing() override;
};

void Subclass1::doACoolThing()
{
    cout << "Hello from the Subclass1" << endl;
}

class Subclass2 : public SuperClass
{
    virtual void doACoolThing() override;
};

void Subclass2::doACoolThing()
{
    cout << "Hello from the Subclass2" << endl;
}

int main()
{
    int userInput;
    cout << "Enter 0, 1 or 2: ";
    cin >> userInput;
    SuperClass *instance = nullptr;
    switch (userInput)
    {
        case 0: 
            instance = new SuperClass();
            break;
        case 1:
            instance = new Subclass1();
            break;
        case 2:
            instance = new Subclass2();
            break;
        default:
            cout << "Unknown input!";
    }

    if (instance)
    {
        instance->doACoolThing();
        delete instance;
    }
    return 0;
}

请考虑以下代码:

Derived1 var1 = <something>;
Derived2 var2 = <something>;
int x;
cin >> x;
Base *baseptr = x ? &var1 : &var2;

baseptr->virtfun();

编译器不知道用户将输入什么,因此无法判断baseptr是否指向Derived1Derived2的实例。

假设您依赖用户输入来决定要创建哪个子类。

class Base
{
public:
   void f();
}
class Derived1: public Base
{
public:
   void f();
}
class Derived2: public Base
{
public:
   void f();
}

int choice;
cin >> choice;

Base *pB = NULL;
if (choice == 1)
{
   pB = new Derived1;
}
else
{
   pB = new Derived2;
}
pB->f();

没有虚函数,如果要根据不同的实例选择f ,编译器如何知道在运行时调用的f的正确版本? 没有办法。

原因包括:

  • 有一些输入(键盘,鼠标,文件,数据库,网络,硬件设备等)仅在运行时已知,它将确定实际的数据类型,从而确定需要调用的成员函数

  • 没有内联并且从具有不同派生对象的许多地方调用的函数将需要虚拟调度 - 静态调度的替代方法相当于每种类型的“实例化”(ala模板,并且隐含一些代码膨胀)

  • 使用虚拟分派的函数可以在链接到可执行文件的对象中编译,并使用指向它们在编译时从未知道的参数类型的指针进行调用

  • 虚拟调度可以提供一种“编译防火墙”(很像指向实现的指针或pImpl习语),这样对定义函数实现的文件的更改不需要更改包含接口的头文件,这意味着客户端代码需要重新链接但不能重新编译:这可以节省企业环境中的大量时间

  • 在程序期间的各个点上跟踪类型通常太复杂了:即使可以从代码的编译时分析中知道,编译器也只是有限地努力识别编译时常量/确定性数据,并且不涉及在最终实际调度呼叫时可能使用的指针到基站的constainer上跟踪任意复杂的操作。

    • 对于容器和实现具有复杂规则的状态机的变量尤其如此,该规则关于何时应删除指针到基础并重置为不同的派生类型

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM