Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 344
Lewis Avatar asked Aug 22 '11 18:08

Lewis


People also ask

Is Virtual necessary C++?

In C++, once a member function is declared as a virtual function in a base class, it becomes virtual in every class derived from that base class. In other words, it is not necessary to use the keyword virtual in the derived class while declaring redefined versions of the virtual base class function.

Why do we need virtual functions in C++?

A virtual function in C++ helps ensure you call the correct function via a reference or pointer. The C++ programming language allows you only to use a single pointer to refer to all the derived class objects.

Can derived class have virtual function?

Functions in derived classes override virtual functions in base classes only if their type is the same. A function in a derived class cannot differ from a virtual function in a base class in its return type only; the argument list must differ as well.

What does the keyword virtual allow us do in C ++?

A C++ virtual function is a member function in the base class that you redefine in a derived class. It is declared using the virtual keyword. It is used to tell the compiler to perform dynamic linkage or late binding on the function.


2 Answers

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
};
like image 70
bdonlan Avatar answered Oct 21 '22 02:10

bdonlan


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

like image 45
sehe Avatar answered Oct 21 '22 04:10

sehe