0

In The C++ Programming Language, 4th Edition, at §20.5.2 "Access to Base Class" (page 605), it says (regarding private inheritance):

private bases are most useful when defining a class by restricting the interface to a base so that stronger guarantees can be provided.For example, B is an implementation detail of Z .The Vector of pointers template that adds type checking to its Vector base from §25.3 is a good example.

It's not clear what Bjarne Stroustrup is trying to say here. How can be a class be defined by restricting "the interface" to a base? What does he mean by "stronger guarantees"?

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
yonutix
  • 1,964
  • 1
  • 22
  • 51

4 Answers4

2

Lets take a very simple example:

// A simple class with a *public* member
class A
{
public:
    int a;
};

// Use private inheritance
class B : private A
{
public:
    int b;
};

// Use public inheritance
class C : public A
{
public:
    int c;
};

// ...

B my_b;
my_b.a = 0;  // Invalid, the member a is private due to the private inhericance

C my_c;
my_c.a = 0;  // Valid, because the inheritance is public

The private inheritance restricts access to the members of the base class. Even if the A::a member variable is public, due to the private inheritance it becomes private in the sub-class B.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
1

Let's stay with the example of the vector. A vector is just a container of Ts. Now let's say you want to build a type that behaves just like a vector, but adds some additional runtime checks. I don't have my copy of TC++PL at hand right now, so let's just make up a constraint: For example, let's say your vector is only allowed to hold even numbers. Attempting to insert an odd number will result in a runtime error. Let's call this new class even_vector and the version without the runtime checks base_vector.

even_vector provides stronger runtime guarantees than base_vector: It is guaranteed that all of its elements are even.

Assuming that your base_vector is designed to work nice as a base class (which std::vector typically does not), you might now be tempted to use public inheritance to implement even_vector in terms of base_vector. After all, the functionality is the same, you simply have some additional runtime checks in the even_vector case on top of the functionality provided by base_vector. However, if you were to use public inheritance here, you would violate the Liskov Substitution Principle: You cannot use an even_vector wherever you use a base_vector. In particular, the even_vector will break in cases where you are inserting odd numbers into a base_vector. This is bad, as now all code that is written for base_vector must account for the fact that some of the base_vectors cannot deal with odd numbers.

With private inheritance you do not have this problem: Here the fact that even_vector inherits from base_vector is a detail of the implementation. Clients cannot use even_vector where a base_vector is expected, so the problem from above does not occur, but we still get the benefits of code reuse.

That being said, using private inheritance for code reuse is a practice that is discouraged by many. An arguably better way would be to use composition here instead, that is, add a private base_vector member to even_vector instead. The advantage of that approach is that it severely reduces coupling between the two classes, as even_vector is no longer able to access any non-public parts of base_vector.

ComicSansMS
  • 51,484
  • 14
  • 155
  • 166
0

how can be a class be defined by restricting "the interface" to a base?

By making the inheritance private. When the inheritance is private, the interface of the base class is restricted to the member functions and not available outside. The access specifier can be given in the list of bases:

class A : private B
//        ^^^^^^^

What does he mean by "stronger guarantees"?

Any guarantee that is not given by the base, or is a superset of a guarantee given by the base.

For example, the guarantee that "behaviour is always well defined" is stronger than "Behaviour is well defined only if input is not null". Another example: "The function does not throw" is stronger than "The function will not throw unless the copy constructor throws".

eerorika
  • 232,697
  • 12
  • 197
  • 326
0

Allow us to look at a possible situation with interfaces to help build up the picture.

class poison {
  public:
    virtual void used() = 0;
};

class food {
  public:
    virtual void eat();
  protected:
    void poisonConsumed(poison& p);
}

class cheese : public food, private poison {
  public:
    virtual void eat() override {
      poisonConsumed(*this);
    }
  private:
    void used() override;
}

This presents cheese to the outside world as 'not a poison' - ie nothing outside the class can know that it's a poison, and it could be made 'not a poison' to no impact on anything using it.

The cheese however, can pass itself to anything expecting a poison which is then free to call used(); even though it's private in cheese.

UKMonkey
  • 6,941
  • 3
  • 21
  • 30