LEMMIIX
LEMMIIX

Reputation: 13

c++ mastering inheritance with public, protected and private

This is what I understand and I encourage you to correct me please: the visibility rules of inheritance of c++ classes are important if classes are inherited multiple times, correct?

class Base {
public :
    int a;
protected : 
    int b;
private :
    int c;
};

class Layer_1 : private Base {  /* everything from ´Base´ is now */
    int z{Base::a};             /* considered a private of ´Layer_1´ */
    int r{Base::b};             /* but accessible here */
};

class Layer_2 : Layer_1 {   /* Nothing in ´Layer_1´ is accessible from */
    int y{Layer_1::a};      /* ´Layer_2´,as ´Layer_1´ inherited everything */
};  // invalid ~~~~^        /* from ´Base´ into their private scope */
  1. Do they have any other purpose?
  2. This has to be used carefully as once I inherit a class as protected I can't go public again, etc. Correct?
  3. If I only have a Base and Layer class, it doesn't really matter how I inherit from Base as I'm not inheriting from Layer, correct?
  4. Are there any known conventions or industry standards how and when to use public/protected/private or has this to be decided on each individual occasion and there's no 'rule'?

I hope this is no duplicate. I've looked but there was no question that answered this specific portion for me.

I got a bit confused and stumped when I couldn't really identify a pattern to how to use the different keywords.

Upvotes: 0

Views: 100

Answers (2)

Quuxplusone
Quuxplusone

Reputation: 26949

The most important thing to know about access control in C++ is that it applies coequally and orthogonally to two different kinds of things:

  • To the class's members (data members, member functions, member typedefs, etc.), i.e., its HAS-A relationships; and,
  • To the class's base-class relationships, i.e. its IS-A relationships.

For example:

class Dolphin : public Animal {
public:
  int dorsal_fin_;
};

A Dolphin HAS-A dorsal fin, and a Dolphin IS-AN Animal; both of these facts are public knowledge, available to anyone in the program. This means any random stranger in the program can write

Dolphin *d = ~~~;
use(d->dorsal_fin_);
Animal *a = d;  // implicit conversion to pointer-to-base

and it'll Just Work. Also, that stranger can access d as if it were an Animal (because it is publicly known to be an Animal) — he can use Animal's public API on d. (Or even, I suppose, use Animal's private API, if Animal is friendly enough toward him.)

Contrary example:

class Dolphin : private Mammal {
private:
  int vestigial_leg_;
};

A Dolphin HAS-A vestigial leg, and a Dolphin IS-A Mammal; but both of these facts are private knowledge, accessible only to class Dolphin itself (and its members and its friends). That means that within Dolphin I can write:

use(d->vestigial_leg_);
Mammal *m = d;  // implicit conversion to pointer-to-base

but a random stranger could not write those lines, because from his point of view he doesn't know that a Dolphin HAS-A vestigial leg and he doesn't know that a Dolphin IS-A Mammal. Those private relationships are inaccessible to him: he's not allowed to exploit them in the code he writes.

IMO everything else falls naturally out of the above, as logical consequences of these two rules.


Finally, there's protected, which means "This relationship is accessible to me (and my members and my friends) and also to my child classes," except that it has some additional fiddly bits around the edges: basically the relationship is accessible to me but only if I'm operating on this instead of on some random pointer I got from someone else. It's very fiddly and in my experience doesn't come up enough to bother with the exact details.


I strongly recommend that you use only a single level of inheritance. Use inheritance for classical polymorphism, in which you have an abstract base class to define the interface and a concrete derived class to define the implementation and then that's it. Don't use "implementation inheritance" if you can at all help it.

I fairly strongly recommend that you never use protected; if you think you need to touch something in a child class, just promote it to public. That is, factor your API carefully so that you can make each API member either 100% public or 100% private. This will complete the virtuous cycle: you'll never have to learn the subtleties of protected because you'll never run into those subtleties because you'll never use it.

Upvotes: 1

Eugene
Eugene

Reputation: 7160

If I only have a Base and Layer class, it doesn't really matter how I inherit from Base as I'm not inheriting from Layer, correct?

No, that is incorrect.

Are there any known conventions or industry standards how and when to use public/protected/private or has this to be decided on each individual occasion and there's no 'rule'?

When you use inheritance for dynamic polymorphism, i.e., if you want "Derived is a Base" relationship, then you must have class Derived : public Base {/*...*/};. This is the main purpose of inheritance, and this is the way it is used in other statically-typed languages (e.g., Java and C#).

If you want the inheritance to be an implementation detail, i.e., if nothing outside Derived would care whether or not it is derived from Base, then use class Derived : private Base {/*...*/};. In most cases, "implemented in terms of" relationship should use containment rather than inheritance, so, instead of class Car : private Engine {/*...*/};, you should use class Car { Engine engine; /*...*/};, which makes the need for private inheritance rare.

Protected inheritance is, indeed, only important if you have more than one level in an inheritance hierarchy. The need for it is exceedingly rare. Do not use it until you get much more experience.

Upvotes: 0

Related Questions