Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Will virtual functions still work if I upcast a derived object to its base type

Tags:

c++

Consider the following:

  • B inherits from A and overrides the print function.
  • A has a static function which takes a void*, casts it to A and calls the virtual print function.
  • If the void* was originally a B will it call A::print or B::print?

    #include <iostream>
    
    class A {
    public:
            static void w(void *p) {
    
                    A *a = reinterpret_cast<A*>(p);
                    a->print();
            }
    
            virtual void print() {
               std::cout << "A"  << std::endl;
            }
    };
    
    class B : public A {
    
    public:
            void print() {
               std::cout << "B"  << std::endl;
            }
    };
    
    int main () {
            B b;
            A::w(&b);
    }
    

This prints B for me.

It seems that the void* which has been casted to an A still knows about B's overridden print function. The reason why is not immediately clear.

Can someone explain to me if this is behavior that I can rely on or if it is just some fluke that works because it is a small example (like how returning a reference to a local variable won't always segfault in small programs).

like image 818
user2675345 Avatar asked Mar 19 '14 11:03

user2675345


2 Answers

your code has undefined behavior

§ 5.2.10 Reinterpret cast

7 Converting a prvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value. The result of any other such pointer conversion is unspecified.

like image 100
billz Avatar answered Jan 01 '23 14:01

billz


Virtual functions are usually resolved by an implicit vtable. This is basically an array of function pointers for every virtual function in the class hierarchy. The compiler adds it as a "hidden member" to your class. When calling a virtual function, the corresponding entry in the vtable is called.

Now, when you create a class of type B, it implicitly has the B-vtable stored in the object. Casts do not affect this table.

Hence, when you cast your void * to A, the original vtable (of class B) is present, which points to B::print.

Note that this is implementation defined behaviour, and the standard does not guarantee anything about this. But most compilers will act like this

like image 23
king_nak Avatar answered Jan 01 '23 12:01

king_nak