1

I was given the following code and asked what it prints:

#include <iostream>
using namespace std;

class C;
class B;

class A {
private:
    B* pointerB;
    C* pointerC;
public:
    A(){ cout << "1"<< endl; }
    A(C* c1): pointerC(c1){ cout << "2"<< endl; }
    A(const A& a): pointerC(a.pointerC){ cout << "3"<< endl; }
    virtual void f() { cout << "4"<< endl; }
    void g() { print(); cout<< "5"<< endl; }
    virtual void h(){ print(); cout<< "6"<< endl; }
    virtual ~A(){ cout << "7"<< endl; }
    virtual void print() { cout << "8"<< endl; }
};

class B: public A {
private:
    int x;
public:
    B(int x1 = 0): x(x1){ cout << "9"<< endl; }
    B(const A& a) : A(a), x(2) { cout << "10"<< endl; }
    B(const B& b): A(b), x(2) { cout << "11"<< endl; }
    virtual void g() { x++; print();}
    void f(){ B::print(); cout << "12"<< endl; }
    void print() { cout << "13"<< x << endl; }
    virtual ~B(){ cout << "14"<< endl; }
};

int main() {
    A* ptrAtoB = new B;
    B* ptrBtoC = new C;
    A* ptrAtoC = new c;
    ptrAtoB->f();
    ptrAtoB->g();
}

for ptrAtoB->f(); we go to class B and execute B::f() function, which uses B::print() that prints 13, then x=0, then B::f() prints 12. However, the next line, I expected ptrAtoB->g(); to do the same, meaning go using B class functions to print 13 1 since we use B::g() to raise x to 1, then print. What ends up happening is that we go to A::g() first, then somehow that uses B::print() to print 13 0, and then back to A::g() to print 5. why is that?

the final output on console is :

130
12
130
5
Yair
  • 99
  • 8
  • 1
    please do not remove includes and using declarations. They can change meaning of code. Strictly speaking this code prints nothing because it does not compile https://godbolt.org/z/ojesozo8P – 463035818_is_not_an_ai Mar 18 '23 at 12:57
  • and please include the output as printed on the console. This will make it easier to refer to the indiviual lines and number being printed. Also you should add some spaces or similar to be able to distinguish eg between `1` and `3` and `13` – 463035818_is_not_an_ai Mar 18 '23 at 13:05
  • 1
    [What is a debugger and how can it help me diagnose problems?](https://stackoverflow.com/questions/25385173/what-is-a-debugger-and-how-can-it-help-me-diagnose-problems) – Jesper Juhl Mar 18 '23 at 13:07
  • `A::g` is not virtual. Is this a typo? – 463035818_is_not_an_ai Mar 18 '23 at 13:13
  • A::g is not virtual, not a typo @463035818_is_not_a_number – Yair Mar 18 '23 at 13:14
  • but thats the reason for the output you do see. I second Jesper, this question can only be answered by stepping through the code line by line and the best way to do this is to use a debugger – 463035818_is_not_an_ai Mar 18 '23 at 13:16
  • "then somehow that uses B::print() to print 13 0, and then back to A::g() to print 5. why is that?" `A::print` is virtual. When you call `print` on a object of type `A` then `A::print` is called. If you call `B::print` then `B::print` is called – 463035818_is_not_an_ai Mar 18 '23 at 13:17
  • i used a debugger. when i step into ptrAtoB->g() it goes directly to A::g(). thats what i dont understand. it must be some virtual keyword or vtable rule i dont know. – Yair Mar 18 '23 at 13:17
  • It goes to `A::g` beause `A::g` is not virtual. THis has nothing to do with the vtable, the vtable is an implementation detail you need not care about. It actually also has nothing to do with virtual. If you call a non virtual method via pointer to `A` then `A`s method is called. – 463035818_is_not_an_ai Mar 18 '23 at 13:19
  • frankly, I think you went to fast too far. You should first make sure you understand why in this example calling `bar` will always print `A` on the screen. https://godbolt.org/z/1hEGcnE46. Once you got that you can add virtual to the mix – 463035818_is_not_an_ai Mar 18 '23 at 13:22
  • ptrAtoB points to a new B, shouldn't it go to B class functions, like it did with B::f()? i thought virtual means that anything that inherits from me can be implemented differently, but here we go higher because of virtual? – Yair Mar 18 '23 at 13:22
  • @Yair you got a pointer to `A` you call `g`, `A::g` is not virtual, `A::g` is called not something else – 463035818_is_not_an_ai Mar 18 '23 at 13:23
  • Why is there a definition of `C` in your **example** code? Don't answer by saying that's how you got the code. Questions here are to be focused. You should reduce the code you have to a [mre], so that there is less to plow through, both for people answering and for people (in the future) with the same question. In a similar vein, drop the lines that produce the first section of output since you understand that. Focus on your question. – JaMiT Mar 18 '23 at 16:08
  • Closest match I found: [Who's function get called when calling f1() through Derived::f2()?](https://stackoverflow.com/q/38402571) -- looks like the same situation (probably; the other question also has a lot of unnecessary code to plow through), but the answer to that question might be overly technical. Another close match is [common function in base and derived calling base when called through another base function using a derived object?](https://stackoverflow.com/q/31354232). – JaMiT Mar 18 '23 at 16:50
  • For an answer, you might want to see [A: Is it legal to declare a method virtual in a derived class when it wasn't virtual in the base?](https://stackoverflow.com/a/15101659) Also worth mentioning is [A: Why does base class pointer to a derived class object with overridden method call a base class method?](https://stackoverflow.com/a/51629445) for the mention of the `override` keyword. – JaMiT Mar 18 '23 at 16:50

1 Answers1

2

Your confusion seems to be that you expect virtual dispatch on a non virtual function. You call g on a pointer to A, A::g is not virtual, hence A::g is called. The fact that B::g is declared virtual does not change the fact that A::g is not virtual. It also does not change the fact that you are calling g via a pointer to A.

A much simpler example reduced only to the detail that seems to cause your confusion:

#include <iostream>

struct A {
    void foo() const {std::cout << "A\n";}
};

struct B : A {
    virtual void foo() const { std::cout << "B\n";}
};

void bar(const A& a) {
    a.foo();
}

int main() {
    bar(A{});
    bar(B{});
}

Try it out. Calling bar will never print something else than A, because A::foo is not virtual.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185