2

As I am practicing C++ with some of my colleagues, one of them "experienced a bug" within a derived class. Long story short, he was calling the base method (callFoo) and he was expecting to be calling A::Foo(), but he got B::Foo() instead.

Here's a complete example of what I am actually asking. I don't really know how to explain better than this. I have indeed found a solution, but that solution is awkward; it makes the code kind of hard to "sanitize" for the future extensions of our classes. Are there other solutions available? (Maybe a keyword?)

n.b. The example is purely demonstrative: in the original example callFoo() was the operator= for Class A. I tried my best to simplify it.

Example 1: Unwanted behavior

#include <iostream>

class A{
    public:
        virtual void Foo(){
            std::cout<<"A Foo\n";
        }

        virtual void callFoo(){
            Foo();
        }
};

class B: public A{
    public:
        virtual void Foo() override{
            std::cout<<"B Foo\n";
        }
};


int main(){
    B mB;

    mB.Foo();
    mB.callFoo();

    return 0;
}

Example 2: The non-ideal fix; are there other solutions?

class A{
    public:
        virtual void Foo(){
            std::cout<<"A Foo\n";
        }

        virtual void callFoo(){
            A::Foo();
        }
};
JaMiT
  • 14,422
  • 4
  • 15
  • 31
DrKGD
  • 23
  • 4
  • 2
    Your colleague is mistaken, this is not a bug. Calling a virtual method from inside a base class method (whether virtual or not itself) will still call an overridden version of the method in a derived class. This is how polymorphism is supposed to work. If the base class wants to call the base class's version of a virtual method, your "fix" is the correct solution. – Remy Lebeau May 02 '20 at 21:04
  • I know it is not a bug, in fact I "solved" it by providing him the solution which you see in the Example 2 @RemyLebeau , I was just wondering if there was another (better?) way to do it. – DrKGD May 02 '20 at 21:06
  • 2
    I'd be interested in seeing how you "fixed" `operator=` rather than a contrived example like this. – Retired Ninja May 02 '20 at 21:06
  • 4
    @DrKGD "*I was just wondering if there was another (better?) way to do it*" - move the wanted base code to a non-virtual method that the base class can call when needed. `class A { private: void doFoo() { std::cout<<"A doFoo\n"; } public: virtual void Foo() { doFoo(); } virtual void callFoo(){ doFoo(); } };` – Remy Lebeau May 02 '20 at 21:10
  • 3
    "Is there another way..." -- there always is another way. This design approach is invalid, and the fact that the author of the code "suspects a bug" where the behaviour is fully correct just proves that the design shall be changed. Try to express the actual problem that you are trying to solve - without employing the details of the language. – Dmitry Kuzminov May 02 '20 at 21:12
  • Wait, while I got to admit my english is not that great, do not linger on the fact that my colleague was experiencing a bug on a common, known thing of virtualization. Sadly I (we) am not in full control of the design pattern of the classes, which are nothing but a recreation (sigh) of std containers (such as Vector, List, Queue (both Vector based and List based implementation), Tree... you get the idea). In reality, the bug was in his logic, he was trying to clear the underlaying circular vector of a QueueVector but he was instead calling the QueueVector::Clear(). – DrKGD May 02 '20 at 21:33
  • @DmitryKuzminov in the end I do not really think there is something to solve, I am kind of new to the language as you'd expect and I was just wondering if the way I solved it was the only way possible. – DrKGD May 02 '20 at 21:37
  • @DrKGD It looks like you neglected to put your actual question in the question. I'll add it now, but feel free to edit it if I misunderstood your intent. – JaMiT May 02 '20 at 22:12
  • 1
    What strikes me about this example is that both `Foo` and `CallFoo` are virtual, which seems like over-virtualization. I would have expected one of them to be non-virtual, something like `CallFoo` being public or protected and non-virtual while `Foo` is private and virtual. Is that something in the original code or is it an artifact of your simplification for this site? – JaMiT May 02 '20 at 22:27

1 Answers1

2

It is clear that you are creating an object of Derived class and trying to invoke callFoo() function present in the base Class. I will try to visualize this step by step :

  1. B mB ---> Since we are creating an object of Derived class , the VPtr will point the to the Vtable of Derived Class.
  2. mB.callFoo(); ---> the Vptr is still pointing to the VTable of the Derived class.Since object is trying to invoke callFoo() , it doesn't found the entry in the VTable of Derived Class .It invokes the callFoo() of Base Class. Now Here is the main catch ,the Foo() is called from the body of callFoo() but again as mentioned earlier it will Search the entry of this function in VTable of the Derived Class and hence it will invoke the Derived class Foo().

Hence we need to explicitly say to compiler that , we need base version of the method and use A::Foo() to call Base class method.

For further details on VTable and VPtr , there is a useful link in stack-overflow: VPtr And VTable In Depth C++

Chandra Shekhar
  • 598
  • 1
  • 7
  • 25
  • 1
    Thank you for the answer, and for the explanation. I was kind of sure about the behavior itself, in fact that is the same explanation I gave to him (guiding him step by step). I was curious about finding "another way to do the same thing but better", reason being he raised the question, hence here I am! – DrKGD May 02 '20 at 21:46
  • 2
    @DrKGD Thanks. In that case as pointed by other members , it seems to be a design issue , you may try to design the class in more efficient way so that the "Compiler's Rule Remain Un-Altered".Because we cannot change the behavior of the compiler , but we can always change our design approach. – Chandra Shekhar May 02 '20 at 21:58
  • 1
    I'd give everyone the "accepted solution", but you were the only one to answer with a post, so I guess thats all yours. Anyway, I really wish we could design the library to our likings but the "project manager and designer" (a.k.a. our professor) would not like it! :P If I had a dime for everything I would have sworn to change in the code... – DrKGD May 02 '20 at 22:05
  • @DrKGD Thanks for the the support.I will suggest decoupling your functionality in multiple classes such that there in minimal inter dependencies of classes can be avoided and also it will make your application quite SCALABLE and EXTENSIBLE. – Chandra Shekhar May 02 '20 at 22:30