calling virtual method without pointing to an object?
Asked Answered
G

2

5
#include <iostream>

struct A {
    void init()
    {
        internal_init();
    }
    virtual void internal_init()
    {
        std::cout << "internal of A" << std::endl;
    }
};

struct B: public A {
    void internal_init()
    {
        init();
        std::cout << "internal of B" << std::endl;
    }
};

int main(){
    B instance;
    std::cout << "internal of A" << std::endl;
    instance.internal_init();
    return 0;
}

First the program goes to B::internal_init() as expected. Then, to A::init() (I guess since B derives from A , and B doesnt have any init()). Now what?

what internal_init() it will choose? since it goes to B::internal_init(),the program will go into and infinite loop, and I don't understand why.

  • What really happens when I call internal_init()?
  • why it calls internal_init() of the "B part" of the instance? Is this about "virtual"? If so, how come? Virtual functions take place when we use polymorphism (which as far as a beginner like my self understands, it's working with pointers of a base class that point to a derived class objects).
Glyptic answered 15/9, 2015 at 14:11 Comment(2)
Note that the first call to internal_init is not polymorphic. But within init(), the call is polymorphic because the (invisible) this pointer is always a pointer... If you want to avoid this, you need to write A::internal_init() (thus forcing a non-polymorphic call) within init.Effluent
This is actually a good example code you got here. It demonstrates that polymorphic calls can be hidden behind non-polymorphic calls. This is necessary to write non-virtual "template methods" (in the sense of the design pattern "template method", not to be confused with C++ templates). There, a non-virtual function calls virtual sub-functions which implement details of a larger scale algorithm. These details can be implemented in sub-classes, while the larger scale algorithm is fixed by the base class.Effluent
O
7

Since instance is a B

instance.internal_init();

Will call Bs internal_init(). Then in internal_init() you call init();. Now member functions have an implicit parameter that is the this pointer.

So when we call A's init() the this pointer is actually a B. In init() we call internal_init(); using the this pointer to a B. Since internal_init() is virtual and we have a pointer to B the virtual lookup mechanism will call B's internal_init().

This then loops again and will eventually cause a segfault or stack overflow.

Optime answered 15/9, 2015 at 14:19 Comment(3)
Could you please explain why "In init() we call internal_init(); using the this pointer to a B so it will call B's internal_init()."?Glyptic
Because that is a polymorphic call. Read the line internal_init() as this->internal_init() (it's actually just a syntactic shorthand), with this of the type A*, but actually pointing to an instance of B. Here the virtual call is resolved to B::internal_init() during runtime.Effluent
@AlexGoft I updated my answer. Let me know if that clears it up for you.Optime
M
2

Firstly struct B inherits all functions of struct A because of struct B: public A. The function internal_init of A is overridden in B because you use the same function signature and the keyword virtual in A.

So now the calls are: instance.internal_init(); which calls internal_init() of B, which calls A::init, which calls B::internal_init(), etc. until a segmentation fault is given. To prevent this (and I think this is what you want), you can explicitly call the internal_init() of A in B instead of calling init():

struct B: public A {
    virtual void internal_init()
    {
        A::internal_init();
        std::cout << "internal of B" << std::endl;
    }
};
Miguelinamiguelita answered 15/9, 2015 at 14:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.