8

I know that it's OK for a pure virtual function to have an implementation. However, why it is like this? Is there conflict for the two concepts? What's the usage? Can any one offer any example?

skydoor
  • 25,218
  • 52
  • 147
  • 201

6 Answers6

7

In Effective C++, Scott Meyers gives the example that it is useful when you are reusing code through inheritance. He starts with this:

struct Airplane {
    virtual void fly() {
        // fly the plane
    }
    ...
};

struct ModelA : Airplane { ... };
struct ModelB : Airplane { ... };

Now, ModelA and ModelB are flown the same way, and that's believed to be a common way to fly a plane, so the code is in the base class. However, not all planes are flown that way, and we intend planes to be polymorphic, so it's virtual.

Now we add ModelC, which must be flown differently, but we make a mistake:

struct ModelC : Airplane { ... (no fly function) };

Oops. ModelC is going to crash. Meyers would prefer the compiler to warn us of our mistake.

So, he makes fly pure virtual in Airplane with an implementation, and then in ModelA and ModelB, put:

void fly() { Airplane::fly(); }

Now unless we explictly state in our derived class that we want the default flying behaviour, we don't get it. So instead of just the documentation telling us all the things we need to check about our new model of plane, the compiler tells us too.

This does the job, but I think it's a bit weak. Ideally we instead have a BoringlyFlyable mixin containing the default implementation of fly, and reuse code that way, rather than putting code in a base class that assumes certain things about airplanes which are not requirements of airplanes. But that requires CRTP if the fly function actually does anything significant:

#include <iostream>

struct Wings {
    void flap() { std::cout << "flapping\n"; }
};

struct Airplane {
    Wings wings;
    virtual void fly() = 0;
};

template <typename T>
struct BoringlyFlyable {
    void fly() {
        // planes fly by flapping their wings, right? Same as birds?
        // (This code may need tweaking after consulting the domain expert)
        static_cast<T*>(this)->wings.flap();
    }
};

struct PlaneA : Airplane, BoringlyFlyable<PlaneA> {
    void fly() { BoringlyFlyable<PlaneA>::fly(); }
};

int main() {
    PlaneA p;
    p.fly();
}

When PlaneA declares inheritance from BoringlyFlyable, it is asserting via interface that it is valid to fly it in the default way. Note that BoringlyFlyable could define pure virtual functions of its own: perhaps getWings would be a good abstraction. But since it's a template it doesn't have to.

I've a feeling that this pattern can replace all cases where you would have provided a pure virtual function with an implementation - the implementation can instead go in a mixin, which classes can inherit if they want it. But I can't immediately prove that (for instance if Airplane::fly uses private members then it requires considerable redesign to do it this way), and arguably CRTP is a bit high-powered for the beginner anyway. Also it's slightly more code that doesn't actually add functionality or type safety, it just makes explicit what is already implicit in Meyer's design, that some things can fly just by flapping their wings whereas others need to do other stuff instead. So my version is by no means a total shoo-in.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • As much as like templates, forcing multiple inheritance on us seems quite annoying. Why not simply provide a template function ? `template void fly(T* t) { t->wings.flap(); }`. I tend to shy from multiple inheritance as far as possible. – Matthieu M. Mar 26 '10 at 09:56
  • It's just that mixins don't play quite as nicely with virtual functions as you'd want. One option is to make BoringlyFlyable inherit from Airplane (perhaps as a template parameter, to keep it flexible), so PlaneA doesn't directly inherit from Airplane, just via `BoringlyFlyable`. That way there's no MI, and PlaneA doesn't have to explicitly define `fly` at all, which is an advantage over both my existing code and your function template `fly_boringly`, that have to be called from `PlaneA`. – Steve Jessop Mar 26 '10 at 16:32
6

Was addressed in GotW #31. Summary:

There are three main reasons you might do this. #1 is commonplace, #2 is pretty rare, and #3 is a workaround used occasionally by advanced programmers working with weaker compilers.

Most programmers should only ever use #1.

... Which is for pure virtual destructors.

Alexander Torstling
  • 18,552
  • 7
  • 62
  • 74
  • I disagree with 2 as pretty rare. I prefer to force the developer to explicitly state that he wishes to use the default. Too many develop by compiling and stop as soon as it does, at least this way I have the hope they may read the excerpt so they know what the default is doing. – Matthieu M. Mar 26 '10 at 09:52
2

There is no conflict with the two concepts, although they are rarely used together (as OO purists can't reconcile it, but that's beyond the scope of this question/answer).

The idea is that the pure virtual function is given an implementation while at the same time forcing subclasses to override that implementation. The subclasses may invoke the base class function to provide some default behavior. The base cannot be instantiated (it is "abstract") because the virtual function(s) is pure even though it may have an implementation.

Brian R. Bondy
  • 339,232
  • 124
  • 596
  • 636
  • @STingRaySC: Thanks for clearing this up to me. I always thought a pure virtual function could not have an implementation. but really it just says that a derived type must override it. And now I can see why my answer did not answer the OPs question. Sorry for my hard headedness in understanding this distinction. – Brian R. Bondy Mar 25 '10 at 20:58
  • @Brian: No worries. It's a little-known thing. Glad you finally got it. –  Mar 25 '10 at 22:56
1

Wikipedia sums this up pretty well:

Although pure virtual methods typically have no implementation in the class that declares them, pure virtual methods in C++ are permitted to contain an implementation in their declaring class, providing fallback or default behaviour that a derived class can delegate to if appropriate.

Typically you don't need to provide base class implementations for pure virtuals. But there is one exception: pure virtual destructors. In fact if your base class has a pure virtual destructor, it must have an implementation. Why would you need a pure virtual destructor instead of just a virtual one? Typically, in order to make a base class abstract without requiring the implementation of any other method. For example, in a class where you might reasonably use the default implementation for any method, but you still don't want people to instantiate the base class, you can mark only the destructor as pure virtual.

EDIT:

Here's some code that illustrates a few ways to call the base implementation:

#include <iostream>
using namespace std;

class Base
{
public:
    virtual void DoIt() = 0;
};

class Der : public Base
{
public:
    void DoIt();
};

void Base::DoIt()
{
    cout << "Base" << endl;
}

void Der::DoIt()
{
    cout << "Der" << endl;
    Base::DoIt();
}

int main()
{
    Der d;
    Base* b = &d;

    d.DoIt();
    b->DoIt();  // note that Der::DoIt is still called
    b->Base::DoIt();


    return 0;
}
Community
  • 1
  • 1
John Dibling
  • 99,718
  • 31
  • 186
  • 324
0

That way you can provide a working implementation but still require the child class implementer to explicitely call that implementation.

Klaim
  • 67,274
  • 36
  • 133
  • 188
0

Well, we have some great answers already.. I'm to slow at writing..

My thought would be for instance an init function that has try{} catch{}, meaning it shouldn't be placed in a constructor:

class A {
public:
    virtual bool init() = 0 {   
        ...  // initiate stuff that couldn't be made in constructor
    }
};

class B : public A{
public:
    bool init(){
        ...
        A::init();
    }
};
default
  • 11,485
  • 9
  • 66
  • 102