Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the usage if I provide an implementation for a pure virtual function in C++

Tags:

c++

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?

like image 354
skydoor Avatar asked Mar 25 '10 19:03

skydoor


People also ask

What are the implications of making a function pure virtual function?

A pure virtual function makes it so the base class can not be instantiated, and the derived classes are forced to define these functions before they can be instantiated. This helps ensure the derived classes do not forget to redefine functions that the base class was expecting them to.

Can a pure virtual function have an implementation?

A pure virtual function (or abstract function) in C++ is a virtual function for which we can have implementation, But we must override that function in the derived class, otherwise the derived class will also become abstract class (For more info about where we provide implementation for such functions refer to this ...

What is the use of pure virtual function in C?

A pure virtual function is a virtual function in C++ for which we need not to write any function definition and only we have to declare it. It is declared by assigning 0 in the declaration. An abstract class is a class in C++ which have at least one pure virtual function.

Why we use virtual functions how they are implemented?

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.


2 Answers

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.

like image 87
Steve Jessop Avatar answered Oct 10 '22 19:10

Steve Jessop


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.

like image 44
Alexander Torstling Avatar answered Oct 10 '22 21:10

Alexander Torstling