How Does Virtual Method Invocation Work in C++

How does virtual method invocation work in C++?

Through virtual tables.

Read this article, http://en.wikipedia.org/wiki/Virtual_table.

I could explain it here, but the wikipedia does a better job than I could.

Why do we need virtual functions in C++?

Without "virtual" you get "early binding". Which implementation of the method is used gets decided at compile time based on the type of the pointer that you call through.

With "virtual" you get "late binding". Which implementation of the method is used gets decided at run time based on the type of the pointed-to object - what it was originally constructed as. This is not necessarily what you'd think based on the type of the pointer that points to that object.

class Base
{
public:
void Method1 () { std::cout << "Base::Method1" << std::endl; }
virtual void Method2 () { std::cout << "Base::Method2" << std::endl; }
};

class Derived : public Base
{
public:
void Method1 () { std::cout << "Derived::Method1" << std::endl; }
void Method2 () { std::cout << "Derived::Method2" << std::endl; }
};

Base* basePtr = new Derived ();
// Note - constructed as Derived, but pointer stored as Base*

basePtr->Method1 (); // Prints "Base::Method1"
basePtr->Method2 (); // Prints "Derived::Method2"

EDIT - see this question.

Also - this tutorial covers early and late binding in C++.

For what purpose non-virtual method will be used with C++?

The answer is very simple : because C++ is not Java.

Programming languages have different philosophies and different ways to accomplish the same result.

Java (and other "OOP-language-where-every-object-is-GCed-and-is-a-reference-type", like C#) encourage you to think about objects in a very specific way: Inheritance and Polymoprphism are the main ways to achieve flexibility and generalization of code. Objects are almost always reference type, meaning that Car car can actually point to Toyota, Ford and whatever. objects are garbaged-collected and dynamically allocated. All objects anyway inherit from an Object class, so inheritance and dynamic polymorphism is anyway imbued into the language objects by the very language design.

C++ is different. the concept of object might be central to the language, but an object is basically a unit of data and functionality. it's a leaner form of a "real" OOP-language object and it usually allocated on the stack, uses RAII to handle its own resources, and is a value type.

Inheritance and Polymorphism exist, but they are inferior to composition and compile-time-polymorphism (templates).

C++ doesn't encourage you to think about objects as a reference type. objects might be a reference type, they might have virtual functions, but this is only one way to achieve flexibility and generalization in C++, as opposed to Java. you might use templates instead, function pointers and opaque types (a-la C style polymorphism), inheritance + overriding function (a-la Java style), Hence, C++ doesn't force you to take the Java route to flexibility - it gives you the opportunity to choose the best way to accomplish things.

Invoking virtual method in constructor: difference between Java and C++

Both approaches clearly have disadvatages:

  • In Java, the call goes to a method which cannot use this properly because its members haven’t been initialised yet.
  • In C++, an unintuitive method (i.e. not the one in the derived class) is called if you don’t know how C++ constructs classes.

Why each language does what it does is an open question but both probably claim to be the “safer” option: C++’s way prevents the use of uninitialsed members; Java’s approach allows polymorphic semantics (to some extent) inside a class’ constructor (which is a perfectly valid use-case).

Calling virtual functions inside constructors

Calling virtual functions from a constructor or destructor is dangerous and should be avoided whenever possible. All C++ implementations should call the version of the function defined at the level of the hierarchy in the current constructor and no further.

The C++ FAQ Lite covers this in section 23.7 in pretty good detail. I suggest reading that (and the rest of the FAQ) for a followup.

Excerpt:

[...] In a constructor, the virtual call mechanism is disabled because overriding from derived classes hasn’t yet happened. Objects are constructed from the base up, “base before derived”.

[...]

Destruction is done “derived class before base class”, so virtual functions behave as in constructors: Only the local definitions are used – and no calls are made to overriding functions to avoid touching the (now destroyed) derived class part of the object.

EDIT Corrected Most to All (thanks litb)

Invoking virtual function and pure-virtual function from a constructor

Do not call pure virtual functions from constructor as it results in Undefined Behavior.

C++03 10.4/6 states

"Member functions can be called from a constructor (or destructor) of an abstract class; the effect of making a virtual call (10.3) to a pure virtual function directly or indirectly for the object being created (or destroyed) from such a constructor (or destructor) is undefined."

You get an compilation error because you have not defined the pure virtual function virtualfunc() in the Base class. To be able to call it, it must have an body.

Anyways, calling pure virtual functions in constructors should be avoided as it is Undefined Behavior to do so.

CLR implementation of virtual method calls via pointer to base class

"TypeHandle" is a popular misnomer. It is actually the MethodTable pointer, the way it is named in the CLR source. The MethodTable of a managed object is functionally very similar to a C++ v-table, a table of pointers to the methods of the class. A small difference is that it doesn't just contain the virtual methods, the table is also used to just-in-time compile a method.

So it just works the exact same way, a simple indirect call against the entries in that table. The jitter knows the offset of the method pointer in the table, just like the C++ compiler does. This runs exactly as fast as native C++ code.

Your snippet written in C# and used like this:

    A obj = new B();
obj.show();

Produces this 32-bit machine code at runtime:

00000003  mov         ecx,51B034Ch            ; typeref for class B           
00000008 call FBB90BF4 ; call operator new

0000000d mov ecx,eax ; setup this pointer
0000000f mov eax,dword ptr [ecx] ; obtain methodtable pointer from object
00000011 call dword ptr [eax+38h] ; indirect call to show()

As you can tell from the offset (0x38), the method table contains more than just the method pointers. You can find details about it in the SSCLI20 source code.

The snippet in native C++ and used like this:

int main()
{
A* obj = new B;
obj->show();
return 0;
}

Produces this 32-bit machine code:

01361010  push        0Ch                     ; size of B object 
01361012 call operator new (136104Ch) ; call operator new
01361017 add esp,4 ; __cdecl stack cleanup
0136101A test eax,eax ; handle null pointer
0136101C je main+1Fh (136102Fh)
0136101E mov dword ptr [eax],offset B::`vftable' (1362100h) ; initialize v-table

01361024 mov edx,dword ptr [eax] ; obtain v-table pointer from object
01361026 mov ecx,eax ; setup this pointer
01361028 mov eax,dword ptr [edx] ; get method pointer
0136102A call eax ; indirect call to show()

The constructor call is a bit more elaborate due to C++ language rules. The method call is the same, just different code-gen. Slightly more efficient on early generation Pentium processors.



Related Topics



Leave a reply



Submit