简体   繁体   English

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

[英]Why are virtual functions handled at runtime?

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

Because we don't always know, what instance we will face at runtime. 因为我们并不总是知道,我们将在运行时面对什么实例。

For example, you have classes: SuperClass , Subclass1 and Subclass2 , and they all have a method doACoolThing() . 例如,您有类: SuperClassSubclass1Subclass2 ,它们都有一个方法doACoolThing() The user presses a button 0 , 1 or 2 , and, depending on his input, an instance of the appropriate class is created, and its doACoolThing() method is called. 在用户按下按钮012 ,并且根据他的输入,创建相应的类的一个实例,其doACoolThing()方法被调用。

There is no way for us (and the compiler too) to figure out what class's method will be called at runtime. 我们(以及编译器)也无法确定在运行时调用哪个类的方法。

That's why such tricks require a runtime support. 这就是为什么这些技巧需要运行时支持。

A small example to illustrate an idea (PS don't write the code like this, it's here just to illustrate polymorphism :) ): 一个小例子来说明一个想法(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;
}

Consider the following code: 请考虑以下代码:

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

baseptr->virtfun();

The compiler doesn't know what the user will input, so it can't tell whether baseptr points to an instance of Derived1 or Derived2 . 编译器不知道用户将输入什么,因此无法判断baseptr是否指向Derived1Derived2的实例。

Say you depend on a user input to decide which of the subclass you want create. 假设您依赖用户输入来决定要创建哪个子类。

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();

Without virtual function, how can the compiler know the correct version of f to call at runtime if you want to select f depending on different instances? 没有虚函数,如果要根据不同的实例选择f ,编译器如何知道在运行时调用的f的正确版本? Just no way. 没有办法。

Reasons include: 原因包括:

  • there's some input (keyboard, mouse, file, database, network, hardware device etc.) only known at runtime which will determine the actual data type and consequently the member functions that need to be invoked 有一些输入(键盘,鼠标,文件,数据库,网络,硬件设备等)仅在运行时已知,它将确定实际的数据类型,从而确定需要调用的成员函数

  • functions that aren't inlined and are called from many places with disparate derived objects will need to dispatch virtually - the alternative for static dispatch amounts to "instantiations" per type (ala templates, and with some code bloat potential implied) 没有内联并且从具有不同派生对象的许多地方调用的函数将需要虚拟调度 - 静态调度的替代方法相当于每种类型的“实例化”(ala模板,并且隐含一些代码膨胀)

  • functions that use virtual dispatch can be compiled in objects that are linked into executables and called with pointers to argument types they never knew about at the time they were compiled 使用虚拟分派的函数可以在链接到可执行文件的对象中编译,并使用指向它们在编译时从未知道的参数类型的指针进行调用

  • virtual dispatch can provide a kind of "compilation firewall" (much like the pointer-to-Implementation or pImpl idiom), such that changes to the files defining the function implementation don't require changes to the headers containing the interfaces, which means client code needs to be relinked but not recompiled: that can save enormous amounts of time in an enterprise environment 虚拟调度可以提供一种“编译防火墙”(很像指向实现的指针或pImpl习语),这样对定义函数实现的文件的更改不需要更改包含接口的头文件,这意味着客户端代码需要重新链接但不能重新编译:这可以节省企业环境中的大量时间

  • it's often just too complicated to keep track of the type at various points during the program: even if it is possible to know from compile-time analysis of the code, the compiler's only obliged to make limited efforts to recognise compile-time constant/deterministic data, and that doesn't involve tracking arbitrarily complex operations on say constainers of pointers-to-base that might be use when a call is eventually virtually dispatched. 在程序期间的各个点上跟踪类型通常太复杂了:即使可以从代码的编译时分析中知道,编译器也只是有限地努力识别编译时常量/确定性数据,并且不涉及在最终实际调度呼叫时可能使用的指针到基站的constainer上跟踪任意复杂的操作。

    • this is especially true of containers, and variables implementing state machines with complex rules about when the pointer-to-base should be deleted and a reset to a different derived type 对于容器和实现具有复杂规则的状态机的变量尤其如此,该规则关于何时应删除指针到基础并重置为不同的派生类型

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

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