Pregunta

Surely the compiler is smart enough to deduce exactly what function you want for some cases, yet how come other cases require run-time support?

¿Fue útil?

Solución

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

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 (P.S. don't write the code like this, it's here just to illustrate polymorphism :) ):

#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;
}

Otros consejos

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.

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? 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)

  • 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

  • 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.

    • 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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top