Template or abstract base class?
Asked Answered
H

9

40

If I want to make a class adaptable, and make it possible to select different algorithms from the outside -- what is the best implementation in C++?

I see mainly two possibilities:

  • Use an abstract base class and pass concrete object in
  • Use a template

Here is a little example, implemented in the various versions:

Version 1: Abstract base class

class Brake {
public: virtual void stopCar() = 0;  
};

class BrakeWithABS : public Brake {
public: void stopCar() { ... }
};

class Car {
  Brake* _brake;
public:
  Car(Brake* brake) : _brake(brake) { brake->stopCar(); }
};

Version 2a: Template

template<class Brake>
class Car {
  Brake brake;
public:
  Car(){ brake.stopCar(); }
};

Version 2b: Template and private inheritance

template<class Brake>
class Car : private Brake {
  using Brake::stopCar;
public:
  Car(){ stopCar(); }
};

Coming from Java, I am naturally inclined to always use version 1, but the templates versions seem to be preferred often, e.g. in STL code? If that's true, is it just because of memory efficiency etc (no inheritance, no virtual function calls)?

I realize there is not a big difference between version 2a and 2b, see C++ FAQ.

Can you comment on these possibilities?

Heartbeat answered 2/3, 2009 at 14:49 Comment(1)
Don't forget to mark stopCar as virtual in their declarations in version 1.Pesade
H
33

This depends on your goals. You can use version 1 if you

  • Intend to replace brakes of a car (at runtime)
  • Intend to pass Car around to non-template functions

I would generally prefer version 1 using the runtime polymorphism, because it is still flexible and allows you to have the Car still have the same type: Car<Opel> is another type than Car<Nissan>. If your goals are great performance while using the brakes frequently, i recommend you to use the templated approach. By the way, this is called policy based design. You provide a brake policy. Example because you said you programmed in Java, possibly you are not yet too experienced with C++. One way of doing it:

template<typename Accelerator, typename Brakes>
class Car {
    Accelerator accelerator;
    Brakes brakes;

public:
    void brake() {
        brakes.brake();
    }
}

If you have lots of policies you can group them together into their own struct, and pass that one, for example as a SpeedConfiguration collecting Accelerator, Brakes and some more. In my projects i try to keep a good deal of code template-free, allowing them to be compiled once into their own object files, without needing their code in headers, but still allowing polymorphism (via virtual functions). For example, you might want to keep common data and functions that non-template code will probably call on many occasions in a base-class:

class VehicleBase {
protected:
    std::string model;
    std::string manufacturer;
    // ...

public:
    ~VehicleBase() { }
    virtual bool checkHealth() = 0;
};


template<typename Accelerator, typename Breaks>
class Car : public VehicleBase {
    Accelerator accelerator;
    Breaks breaks;
    // ...

    virtual bool checkHealth() { ... }
};

Incidentally, that is also the approach that C++ streams use: std::ios_base contains flags and stuff that do not depend on the char type or traits like openmode, format flags and stuff, while std::basic_ios then is a class template that inherits it. This also reduces code bloat by sharing the code that is common to all instantiations of a class template.

Private Inheritance?

Private inheritance should be avoided in general. It is only very rarely useful and containment is a better idea in most cases. Common case where the opposite is true when size is really crucial (policy based string class, for example): Empty Base Class Optimization can apply when deriving from an empty policy class (just containing functions).

Read Uses and abuses of Inheritance by Herb Sutter.

Hectocotylus answered 2/3, 2009 at 15:31 Comment(2)
By Breaks I assume you meant Brakes? :)Provided
+1, good point about Car<X> and Car<Y> being different types.Irremediable
P
32

The rule of thumb is:

1) If the choice of the concrete type is made at compile time, prefer a template. It will be safer (compile time errors vs run time errors) and probably better optimized. 2) If the choice is made at run-time (i.e. as a result of a user's action) there is really no choice - use inheritance and virtual functions.

Pledget answered 2/3, 2009 at 15:10 Comment(2)
I can't agree with your second point. You can allways chose what kind of the template instantiation to create based on a user input.Metamorphose
Using if statements, I guess (though not very elegant ...)Heartbeat
F
6

Other options:

  1. Use the Visitor Pattern (let external code work on your class).
  2. Externalize some part of your class, for example via iterators, that generic iterator-based code can work on them. This works best if your object is a container of other objects.
  3. See also the Strategy Pattern (there are c++ examples inside)
Forgat answered 2/3, 2009 at 14:58 Comment(0)
W
5

Templates are a way to let a class use a variable of which you don't really care about the type. Inheritance is a way to define what a class is based on its attributes. Its the "is-a" versus "has-a" question.

Wera answered 2/3, 2009 at 14:57 Comment(0)
P
4

Most of your question has already been answered, but I wanted to elaborate on this bit:

Coming from Java, I am naturally inclined to always use version 1, but the templates versions seem to be preferred often, e.g. in STL code? If that's true, is it just because of memory efficiency etc (no inheritance, no virtual function calls)?

That's part of it. But another factor is the added type safety. When you treat a BrakeWithABS as a Brake, you lose type information. You no longer know that the object is actually a BrakeWithABS. If it is a template parameter, you have the exact type available, which in some cases may enable the compiler to perform better typechecking. Or it may be useful in ensuring that the correct overload of a function gets called. (if stopCar() passes the Brake object to a second function, which may have a separate overload for BrakeWithABS, that won't be called if you'd used inheritance, and your BrakeWithABS had been cast to a Brake.

Another factor is that it allows more flexibility. Why do all Brake implementations have to inherit from the same base class? Does the base class actually have anything to bring to the table? If I write a class which exposes the expected member functions, isn't that good enough to act as a brake? Often, explicitly using interfaces or abstract base classes constrain your code more than necessary.

(Note, I'm not saying templates should always be the preferred solution. There are other concerns that might affect this, ranging from compilation speed to "what programmers on my team are familiar with" or just "what I prefer". And sometimes, you need runtime polymorphism, in which case the template solution simply isn't possible)

Provided answered 2/3, 2009 at 16:0 Comment(1)
+1, good point about overloads on derived types not being called when the object is held via reference/pointer to base. OTOH, a common use case for using a base class is "I want to have a vector<> that contains all my Brakes and call stopCar() on each one."Irremediable
D
3

this answer is more or less correct. When you want something parametrized at compile time - you should prefer templates. When you want something parametrized at runtime, you should prefer virtual functions being overridden.

However, using templates does not preclude you from doing both (making the template version more flexible):

struct Brake {
    virtual void stopCar() = 0;
};

struct BrakeChooser {
    BrakeChooser(Brake *brake) : brake(brake) {}
    void stopCar() { brake->stopCar(); }

    Brake *brake;
};

template<class Brake>
struct Car
{
    Car(Brake brake = Brake()) : brake(brake) {}
    void slamTheBrakePedal() { brake.stopCar(); }

    Brake brake;
};


// instantiation
Car<BrakeChooser> car(BrakeChooser(new AntiLockBrakes()));

That being said, I would probably NOT use templates for this... But its really just personal taste.

Drapery answered 2/3, 2009 at 15:54 Comment(0)
K
2

Abstract base class has on overhead of virtual calls but it has an advantage that all derived classes are really base classes. Not so when you use templates – Car<Brake> and Car<BrakeWithABS> are unrelated to each other and you'll have to either dynamic_cast and check for null or have templates for all the code that deals with Car.

Kimura answered 2/3, 2009 at 14:59 Comment(0)
M
0

Use interface if you suppose to support different Break classes and its hierarchy at once.

Car( new Brake() )
Car( new BrakeABC() )
Car( new CoolBrake() )

And you don't know this information at compile time.

If you know which Break you are going to use 2b is right choice for you to specify different Car classes. Brake in this case will be your car "Strategy" and you can set default one.

I wouldn't use 2a. Instead you can add static methods to Break and call them without instance.

Marginal answered 2/3, 2009 at 14:59 Comment(0)
M
0

Personally I would allways prefer to use Interfaces over templates because of several reasons:

  1. Templates Compiling&linking errors are sometimes cryptic
  2. It is hard to debug a code that based on templates (at least in visual studio IDE)
  3. Templates can make your binaries bigger.
  4. Templates require you to put all its code in the header file , that makes the template class a bit harder to understand.
  5. Templates are hard to maintained by novice programmers.

I Only use templates when the virtual tables create some kind of overhead.

Ofcourse , this is only my self opinion.

Metamorphose answered 10/4, 2009 at 10:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.