flashburn
flashburn

Reputation: 4528

non virtual method call in a virtual method, C++

Here is the code I'm talking about

#include <stdio.h>
#include <stdlib.h>

class A {
public:
  void method() {printf("method A\n");}
  virtual void parentMethod() { printf("parentMethod\n"); method(); }
};

class B : public A {
public:
  void method() {printf("method B\n");}
};

int main(void) {
  A a;
  B b;

  a.parentMethod();
  b.parentMethod();
}

My question is why this is happening? Why when b.parentMethod() is called it doesn't print method B. I realize that it has something to do with method() being in A and B as well as being non-virtual, but I can't get my head around it. Would someone be able to explain this behaviour?

Any help is appreciated.

Upvotes: 1

Views: 116

Answers (4)

user3427419
user3427419

Reputation: 1779

My question is why this is happening? Why when b.parentMethod() is called it doesn't print method B. I realize that it has something to do with method() being in A and B as well as being non-virtual, but I can't get my head around it. Would someone be able to explain this behaviour?

C++ has two levels of indirection when it comes to classes/structures. You have your "plain functions" (including "overloaded", "lambdas", static, etc.) and you have your "virtual functions". First, let's explain "plain functions".

struct Foo {
    void goo();
};

In this structure, goo is just a plain old functions. If you try to write it in C, this would be analogous to calling,

void goo(struct Foo *this);

Nothing magical, just a plain function with a "hidden" pointer (all c++ functions have that "hidden" this pointer passed to them). Now, let's re-implement this function in an inherited structure,

struct Goo : public Foo {
    void goo();
};
...
Goo g;
g.goo();

Foo f;
f.goo();

Here, plain as day, g.goo() calls goo() in Goo structure, and f.goo() calls goo() in Foo structure. So, in C functions, this would be just,

void goo(struct Foo *this);
void goo(struct Goo *this);

provided C did parameter overloading. But still, just plain functions. Calling goo() in Foo object will call different function than calling goo() in Goo object. Compile time resolution only. But now, let's make our function "virtual"

struct Foo {
    virtual void goo();
};

struct Goo : public Foo {
    void goo();  // <- also virtual because Foo::goo() is virtual
                 // In C++11 you'll want to write
                 //     void goo() override;
                 // which verifies that you spelled function name correctly
                 // and are not making *new* virtual functions! common error!!
};
...
Goo g;
g.goo();

Foo f;
f.goo();

What happens here is that Foo now contains a "virtual table". The compiler now creates a table of functions that maps location of "latest" goo() function. Namely, implicit Foo() constructor would do something like,

virt_table[goo_function_idx] = &Foo::goo;

and then the constructor in Goo() would update this table with,

virt_table[goo_function_idx] = &Goo::goo;

And then when you have,

Foo *f = new Goo();
f->goo();

what happens is akin to,

f->virt_table[goo_function_idx]();

The function location is looked up in the "virtual table", and that function is called. This means runtime resolution of functions, or polymorphism. And this is how Goo::goo() is called.

Without this table, the compiler can only call functions it knows for said object. So in your example, b.parentMethod() is looked up in the table and called. But method() is not part of that table, so only compile-time resolution is attempted. And since this pointer is A*, you get A::method called.

I hope this clears up the "virtual table" business - it's literally an internal lookup table, but only for functions marked as virtual!

PS. You may ask, "but the this pointer will get 'upcast' by the virtual table from Foo* to Goo*", and yes, it would. I'll leave it as an exercise for you to figure out why that would always be correct.

Upvotes: 2

erip
erip

Reputation: 16945

virtual means that a method can be overridden in a subclass. I think you wanted method, not parentMethod, to be overridden for B. I've renamed parentMethod to foo to be less misleading.

#include <stdio.h>
#include <stdlib.h>

class A {
public:
  virtual void method() {printf("method A\n");}
  void foo() { printf("foo\n"); method(); }
};

class B : public A {
public:
  void method() {printf("method B\n");}
};

int main(void) {
  A a;
  B b;

  a.foo();
  b.foo();
}

You'll see that this gives the expected output:

foo
method A
foo
method B

Here's the ideone.

Upvotes: 1

Ari0nhh
Ari0nhh

Reputation: 5920

Well you are correct. This is happening because your method is not virtual. When you are calling it through the parent class, there is no way for it to know, that it was overloaded, so A::method is always called. If you mark method as virtual, then call to it will be routed through the class vtable, so A::method would be replaced by the B::method in the ascendant class.

Upvotes: 1

Thomas Moulard
Thomas Moulard

Reputation: 5542

You code was missing a virtual keyword:

#include <stdio.h>
#include <stdlib.h>

class A {
public:
  virtual void method() {printf("method A\n");} // virtual was missing
  void parentMethod() { printf("parentMethod\n"); method(); } // unnecessary virtual keyword
};

class B : public A {
public:
  void method() {printf("method B\n");}
};

int main(void) {
  A a;
  B b;

  a.parentMethod();
  b.parentMethod();
}

The definition in the most upper class must contain the virtual keyword. It is very logical if you think about it. In this case, when you call method() the compiler knows it has to do something more than with a normal function call immediately. Otherwise, it would have to find and iterate on all the derived types to see if they contain a redefinition of method().

Upvotes: 2

Related Questions