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 */
protected
I can't go public
again, etc. Correct?Base
and Layer
class, it doesn't really matter how I inherit from Base
as I'm not inheriting from Layer
, correct?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.
The most important thing to know about access control in C++ is that it applies coequally and orthogonally to two different kinds of things:
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.