Does C++ allow multiple levels of virtualness?

By : Lewis
Source: Stackoverflow.com
Question!

I have a base class called Object. PhysicsObject inherits from Object. Ball inherits from PhysicsObject, and SoftBall inherits from Ball. Something like this:

Object
 |
PhysicsObject
 |
Ball
 |
SoftBall

I have a method called foo() that is declared virtual in Object (given an implementation, so not pure virtual), then declared and implemented as virtual again in PhysicsObject and Ball. Finally, SoftBall implements foo() again without declaring it as virtual.

If I have an Object* that points to a SoftBall, will SoftBall's version of foo() be called? If not, is there any way to achieve this effect? Basically, does this aspect of polymorphism still work over more than one level of inheritance?

By : Lewis


Answers

If I have an Object* that points to a SoftBall, will SoftBall's version of foo() be called? If not, is there any way to achieve this effect? Basically, does this aspect of polymorphism still work over more than one level of inheritance?

Yes.

Of course, you could simply have tried it... ;)


For posterity, here's my testcase (which assumes cout is in scope):

struct Object {
   virtual void foo() {}
};

struct PhysicsObject : Object {};
struct Ball : PhysicsObject {};

struct SoftBall : Ball {
   virtual void foo() {
      cout << "SoftBall";
   }
};

int main() {
   SoftBall sb;
   Object* o = &sb;
   o->foo();
}

// Output: "SoftBall"


If I have an Object* that points to a SoftBall, will SoftBall's version of foo() be called? If not, is there any way to achieve this effect? Basically, does this aspect of polymorphism still work over more than one level of inheritance?

Yes

C++ defines methods as virtual if the corresponding overload is virtual in the base class (unlike other OO languages, where overrides have to be marked explicitely).

http://codepad.org/pL09QWNN


Note that with some more ingredients you can get really funky:

#include <iostream>

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

struct Oa: A, B { using A::foo; };
struct Ob: A, B { using B::foo; };

#define TEST(a) std::cout << #a << ":\t"; a

int main()
{
    A a;
    B b;
    Oa oa;
    Ob ob;

    TEST(a.foo());
    TEST(b.foo());
    TEST(oa.foo());
    TEST(ob.foo());

    std::cout << "But oa through different references:" << std::endl;
    {
        A& ar = oa;
        TEST(ar.foo());

        B& br = oa;
        TEST(br.foo());
    }

    std::cout << "And ob through different references:" << std::endl;
    {
        A& ar = ob;
        TEST(ar.foo());

        B& br = ob;
        TEST(br.foo());
    }

    return 0;
}

Try to predict what gets printed here. It get's a lot more fun once you mixin:

  • (mutable/const/volatile qualified) overloads
  • (conflicting) default arguments

I remember reading quite a lot of more and less horrendous examples in the form of trivia question I hope to never encounter in an interview. Although I know what I'd answer: "If you have code like this, I'm not sure I want the job"?.

You could go looking on Herb Sutter, Scott Meyer, and you'll be amazed what pitfalls lurk in our otherwise-so-civilized-nice-little-language-fondly-referred-to-as C++

By : sehe


If you declare a virtual function in a base class, and declare a function with the same signature in a derived class, it will automatically be made virtual (even with any number of layers of classes in between). So the answer to your question is, yes, the SoftBall implementation of foo() will be called.

Beware, however, that if there is any difference in the signature, then the virtual function will be hidden instead. For example:

class Object {
public:
  virtual void foo();
};

class InBetween : public Object {
  // assume it has no foo()s
};

class A : public InBetween {
public:
  void foo() const; // DOES NOT OVERRIDE; ((Object *)pA)->foo() calls Object::foo()
                    // However pA->foo() calls this
};

class B: public InBetween {
public:
  void foo() const; // Does not override; pB->foo() calls this if pB is const
  void foo(); // Does override; ((Object *)pB)->foo() calls this. pB->foo() also calls this if pB is non-const
};
By : bdonlan


This video can help you solving your question :)
By: admin