Protected constructor to make base class not instantiable
Asked Answered
C

4

7

Is it good practice to make a base class constructor protected if I want to avoid instances of it? I know that I could as well have a pure virtual dummy method, but that seems odd...

Please consider the following code:

#include <iostream>

using std::cout;
using std::endl;

class A 
{
protected:
    A(){};
public:
    virtual void foo(){cout << "A\n";};
};

class B : public A
{
public:
    void foo(){cout << "B\n";}
};

int main()
{
    B b;
    b.foo();
    A *pa = new B;
    pa->foo();

    // this does (and should) not compile:
    //A a;
    //a.foo();
    return 0;
}

Is there a disadvantage or side-effect, that I don't see?

Corissa answered 30/9, 2013 at 9:8 Comment(0)
T
10

It is common practice to make base class constructors protected. When you have a pure-virtual function in your base class, this is not required, as you wouldn't be able to instantiate it.

However, defining a non-pure virtual function in a base class is not considered good practice, but heavily depends on your use case and does not harm.

There isn't any disadvantage or side-effect. With a protected constructor you just tell other developers that your class is only intended to be used as a base.

Theocentric answered 30/9, 2013 at 9:18 Comment(2)
Depends on the use case, but in big projects with heavy class hierarchies and complex algorithms and lots of developers that could introduce side effects difficult too debug.Theocentric
For example, when using C++ for safety critical software, overwriting functions (virtual and non-virtual) shall not be used, because that makes things more difficult to verify.Theocentric
M
2

What you want to achieve is normally done via the destructor, instead of the constructors, just beacause you can steer the behavior you need with that one function instead of having to deal with it with every new constructor you write. It's a common coding style/guideline, to

  • make the destructor public, if you want to allow instances of the class. Often those classes are not meant to be inherited from.
  • make the destructor pure virtual and public, if you want to use and delete the class in a polymorphic context but don't want to allow instances of the class. In other words, for base classes that are deleted polymorphcally.
  • make the destructor nonvirtual and protected, if you don't want to allow instances of the class and don't want to delete its derivates polymorphically. Normally, you then don't want to use them polymorphically at all, i.e. they have no virtual functions.

Which of the latter two you chose is a design decision and cannot be answered from your question.

Milk answered 30/9, 2013 at 10:22 Comment(3)
If you mark a destructor as pure virtual (virtual ~A() = 0;) you must also still provide an implementation of it outside the class definition: A::~A() { }. When an object is destroyed, all base class destructors are always implicitly called after the concrete class' destructor, even in the case of polymorphic deletion.Exanthema
@Exanthema yes of course, one always has to provide (or let the compiler generate) destructor definitions to be able to use the class. Exceptions are traits and similar classes that exist solely for their static members, e.g. in metaprogramming.Milk
Yes, I figured you knew that but I wanted to leave a note for anyone who saw your answer and didn't realize it. Many less experienced C++ programmers are under the false understanding that pure virtual functions can't have implementations.Exanthema
P
1

It does what you're asking.

However I'm not sure what you are gaining with it. Someone could just write

struct B : A {
   // Just to workaround limitation imposed by A's author
};

Normally it's not that one adds nonsense pure-virtual functions to the base class... it's that there are pure virtual functions for which no meaningful implementation can be provided at the base level and that's why they end up being pure virtual.

Not being able to instantiate that class comes as a nice extra property.

Pirogue answered 30/9, 2013 at 9:14 Comment(2)
That is the intention: I want only derived classes to be instantiated.Corissa
@steffen: just because, I suppose.Pirogue
S
1

Make the destructor pure virtual. Every class has a destructor, and a base class should usually have a virtual destructor, so you are not adding a useless dummy function.

Take note that a pure virtual destructor must have a function body.

class AbstractBase
{
public:
    virtual ~AbstractBase() = 0;
};
inline AbstractBase::~AbstractBase() {}

If you don't wish to put the destructor body in the header file, put it in the source file and remove inline keyword.

Selfrenunciation answered 30/9, 2013 at 9:28 Comment(3)
That's an interesting one... The bad thing is that now I have to decide. As usual: there is more than one way to do it ;)Corissa
Pure virtual destructors are only viable if the class is meant to be used polymorphically. Using a pure virtual destructor in a class that has no other virtual functions introduces an unnecessary overhead.Milk
@ArneMertz True but the original example also contains a virtual function.Selfrenunciation

© 2022 - 2024 — McMap. All rights reserved.