“Direct” vs “virtual” call to a virtual function

前端 未结 2 1153
旧时难觅i
旧时难觅i 2021-02-14 13:31

I am self-taught, and therefore am not familiar with a lot of terminology. I cannot seem to find the answer to this by googling: What is a \"virtual\" vs a \"direct\" call to a

相关标签:
2条回答
  • 2021-02-14 13:40

    The answer to your question is different at different conceptual levels.

    • At conceptual language level the informal term "virtual call" usually refers to calls resolved in accordance with the dynamic type of the object used in the call. According to C++ language standard, this applies to all calls to virtual functions, except for calls that use qualified name of the function. When qualified name of the method is used in the call, the call is referred to as "direct call"

      SomeObject obj;
      SomeObject *pobj = &obj;
      SomeObject &robj = obj;
      
      obj.some_virtual_function(); // Virtual call
      pobj->some_virtual_function(); // Virtual call
      robj.some_virtual_function(); // Virtual call
      
      obj.SomeObject::some_virtual_function(); // Direct call
      pobj->SomeObject::some_virtual_function(); // Direct call
      robj.SomeObject::some_virtual_function(); // Direct call
      

      Note that you can often hear people say that calls to virtual functions made through immediate objects are "not virtual". However, the language specification does not support this point of view. According to the language, all non-qualified calls to virtual functions are the same: they are resolved in accordance with the dynamic type of the object. In that [conceptual] sense they are all virtual.

    • At implementation level the term "virtual call" usually refers to calls dispatched through some implementation-defined mechanism, that implements the standard-required functionality of virtual functions. Typically it is implemented through Virtual Method Table (VMT) associated with the object used in the call. However, smart compilers will only use VMT to perform calls to virtual functions when they really have to, i.e. when the dynamic type of the object is not known at compile time. In all other cases the compiler will strive to call the method directly, even if the call is formally "virtual" at the conceptual level.

      For example, most of the time, calls to virtual functions made with an immediate object (as opposed to a pointer or a reference to object) will be implemented as direct calls (without involving VMT dispatch). The same applies to immediate calls to virtual functions made from object's constructor and destructor

      SomeObject obj;
      SomeObject *pobj = &obj;
      SomeObject &robj = obj;
      
      obj.some_virtual_function(); // Direct call
      pobj->some_virtual_function(); // Virtual call in general case
      robj.some_virtual_function(); // Virtual call in general case
      
      obj.SomeObject::some_virtual_function(); // Direct call
      pobj->SomeObject::some_virtual_function(); // Direct call
      robj.SomeObject::some_virtual_function(); // Direct call
      

      Of course, in this latter sense, nothing prevents the compiler from implementing any calls to virtual functions as direct calls (without involving VMT dispatch), if the compiler has sufficient information to determine the dynamic type of the object at compile time. In the above simplistic example any modern compiler should be able to implement all calls as direct calls.

    0 讨论(0)
  • 2021-02-14 13:49

    Suppose you have this class:

    class X { 
    public: 
        virtual void myfunc(); 
    };  
    

    If you call the virtual function for a plain object of type X, the compiler will generate a direct call, i.e. refer directly to X::myfunct():

    X a;         // object of known type 
    a.myfunc();  // will call X::myfunc() directly    
    

    If you'd call the virtual function via a pointer dereference, or a reference, it is not clear which type the object pointed to will really have. It could be X but it could also be a type derived from X. Then the compiler will make a virtual call, i.e. use a table of pointers to the function address:

    X *pa;        // pointer to a polymorphic object  
    ...           // initialise the pointer to point to an X or a derived class from X
    pa->myfunc(); // will call the myfunc() that is related to the real type of object pointed to    
    

    Here you have an online simulation of the code. You'll see that in the first case, the generated assembly calls the address of the function, whereas in the second case, the compiler loads something in a register and make an indirect call using this register (i.e. the called address is not "hard-wired" and will be determined dynamically at run time).

    0 讨论(0)
提交回复
热议问题