开发者

Does C++ allow multiple levels of virtualness?

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开发者_C百科 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?


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
};


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++


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"
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜