5
votes

I know this question must have been covered endless of times, but I've searched the previous questions, and nothing seems to pop.

It's about inheritance and virtual functions i C++. I have a problem with calling virtual functions in subclasses from the superclass.

Let me give an example. Start of with three classes, which inherit from each other.

class A {

    void foo() { bar() }
    virtual void bar() { }

};

class B : public A {

    virtual void bar() { }

};

class C : public B {

    virtual void bar() { // do something }

};

Now I wanna have a variable declared as B* but instantiated as C*.

B* myObject = new C();
myObject->foo();

When I do this, and call foo() on myObject, then A::foo() is calling bar(). But only B::bar() is called, not C::Bar() - which in reality myObject is, even though it's declared as B, which again affects that "// do nothing" doesn't get executed.

How do I tell A::foo(), that it needs to look at lowest implementation?

Makes sense?

// Trenskow

EDIT:

C::Foo is not the problem. Foo is being called in class A, as it's the only place it's implemented. The problem arises, when A:Foo calls Bar(). Then B:Bar is called and not C::Bar.

Maybe the problem is, that in my implementation, I only get a void* pointer to the object in A.

Like this:

void A:Foo(void *a) {

    A* tmpA = static_cast<A*> (a);
    tmpA->bar();

}

Now the compiler thinks, that tmpA is an A. But somehow it manages to figure that it's a B*, and calls B::Bar, when in fact tmpA is a C* and it should be calling C::Bar.

6
If you posted real code then we could tell that the problem is. You don't need to do anything special. If the syntax errors are fixed, the code (myObject->foo();) will call C::bar(). - UncleBens
... but if I were to guess, either C does not really override bar (having a different signature), or you are slicing the object somewhere (by doing something like *variable = *myObject; - UncleBens
I still don't buy it. Why wouldn't C::bar() be called? Can you post the actual code? - EboMike
You probably should try to create a minimum example that exhibits the problem. If you can't reproduce the results, compare your minimal example to what your code is doing. Chances are you'll figure it out yourself. - UncleBens
I'm having a similar problem, and I have no clue on why this happens. I've experienced it before, but rewriting classes from scratch seem to have fixed it. At the moment, I'm looking for a clue, so I came across this. There must be someone somewhere who thinks: "I experienced that and found the solution; you're probably doing <this and that> wrong." -If so, please post a clue. - user1985657

6 Answers

5
votes

The following prints "A::foo C::bar" as expected. Are you getting something different? B::bar is never called because C is the actual runtime type of the object. In C::bar, you could call B::bar explicitly by adding B::bar(); to its body.

#include <iostream>
using namespace std;

class A {
public:
    void foo() { cout << "A::foo "; bar(); }
    virtual void bar() { }
};

class B : public A {
public:
    virtual void bar() { cout << "B::bar" << endl; }
};

class C : public B {
public:
    virtual void bar() { cout << "C::bar" << endl; }
};

int main()
{
    B* c = new C();
    c->foo();
    return 0;
}
2
votes
void A:Foo(void *a) {

    A* tmpA = static_cast<A*> (a);
    tmpA->bar();

}

This is undefined behaviour. You cannot cast a B* to a void*, then cast that void* back to an A*. If you want it to work properly, you have to ditch the void*. Alternatively, you could try dynamic_cast.

0
votes

Assuming you mistyped your last block of code and the names match:

B* variable = new C();
variable->foo();

Then the C::Foo method is being called or you are using a terribly bad compiler.

(This also assumes that you don't actually have a compiler error in C::Foo, and that the comment is actually something like std::cout << "Hi mom!" << std::endl;)

0
votes

Don't you mean:

B* myObject = new C();
myObject->foo(); // not variable->foo()

class A
{
public:
    void foo() { bar(); }
    virtual void bar() { std::cout << "A"; };
};

class B : public A
{
public:
    virtual void bar() { std::cout << "B";};
};

class C : public B
{
public:
    virtual void bar() { std::cout << "C"; }
};

This prints 'C' as expected.

0
votes

I don't follow. You're saying

But only B::bar() is called, not C::Bar()

No. You invoked the constructor of class C, which means that the vtable makes bar() point to C::bar(), so calling foo() in this case would go straight to C::bar().

If you want to force A::foo() to explicitly only call A's implementation, you can do that by just writing

void foo() { A::bar(); }

What exactly are you trying to do?

0
votes

what compiler are you using? Visual Studio (IIRC) usually has the runtime type information turned off by default so maybe its just something simple as that?