I haven't seen anything like this in my research, so if this is already a known thing, please link me to the articles on it.
Nearly everyone will tell you that private inheritance has limited uses and is mainly only for composition/has-a relationships and implemented-in-terms-of relationships, but I think everyone is missing out on a big useful feature that comes with private inheritance: private interfaces.
Generally in C++ (and always in Java), interfaces are implemented publicly. However, this means that any code that has access to an instance of the class also has access to all the interfaces the class implements. People will tell you "that's good, that's how it should be", but I disagree - there are definitely cases where I only want a certain interface to my class to be accessible to specific code. Mediator objects sort of accomplish this, but they seem convoluted to me. Why not take the simple approach?
C++ private inheritance is exactly that: a private interface. Only the class implementing the interface is allowed to cast a reference to the class into a reference to the interface, so the class has full control over which code it gives access to that interface to. Obviously, this goes hand-in-hand with tell-don't-ask.
This also allows for much more modular coupling - coupled classes can communicate through their private interfaces and know that only the two interfaces know about each other, and not just arbitrary code. Where a mediator needs to know of all classes/interfaces it mediates, each private interface need only know about the other private interfaces it interacts with, and not all that are involved. There's also no use of the
friend
keyword.
Here's an example:
1 2 3 4 5 6 7 8 9 10 11
|
//MoveReceiver.hpp
struct Position;
struct GamePiece;
struct MoveReceiver
{
virtual ~MoveReceiver() = default;
virtual void addMove(GamePiece &piece, Position const &m) = 0;
virtual void removeMove(GamePiece &piece, Position const &m) = 0;
};
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
//GameBoard.hpp
#include "MoveReceiver.hpp"
#include <memory>
#include <set>
#include <map>
struct GameBoard : private MoveReceiver
{
void addPiece(Position const &p);
//...
private:
std::set<std::unique_ptr<GamePiece>> pieces;
std::multimap<Position, decltype(pieces)::iterator> moves;
virtual void addMove(GamePiece &piece, Position const &m) final override;
virtual void removeMove(GamePiece &piece, Position const &m) final override;
};
|
1 2 3 4 5 6 7 8 9
|
//GamePiece.hpp
#include "GameBoard.hpp"
struct GamePiece
{
GamePiece(MoveReceiver &mr);
//...
};
|
I assume that is enough information to understand how it works and what is going on, but I can elaborate on anything and provide example implementations of the declared member functions or explain the intended interactions.
Only a
GamePiece should decide which moves it can make, and only a
GameBoard needs to know all valid moves at once. Thus, a
GamePiecce receives the exact interface it needs, and a
GameBoard only gives access to that interface to
GamePieces. The private interface here is, obviously,
MoveReiever. Yes, one instance of a
GamePiece can submit move instructions for different instances of
GamePiece, but in this example the interfaces are designed such that each
GamePiece has no way to be aware of other
GamePieces. If needed, each
GamePiece can be made aware only of the
Positions of other
GamePieces so as to change its moveset accordingly.
Private interfaces solve a lot of problems I've been having with my designs, so I find them very useful. But what do you think?