What is the difference between public, private, and protected inheritance in C++?
Solution 1
To answer that question, I'd like to describe member's accessors first in my own words. If you already know this, skip to the heading "next:".
There are three accessors that I'm aware of: public
, protected
and private
.
Let:
class Base {
public:
int publicMember;
protected:
int protectedMember;
private:
int privateMember;
};
- Everything that is aware of
Base
is also aware thatBase
containspublicMember
. - Only the children (and their children) are aware that
Base
containsprotectedMember
. - No one but
Base
is aware ofprivateMember
.
By "is aware of", I mean "acknowledge the existence of, and thus be able to access".
next:
The same happens with public, private and protected inheritance. Let's consider a class Base
and a class Child
that inherits from Base
.
- If the inheritance is
public
, everything that is aware ofBase
andChild
is also aware thatChild
inherits fromBase
. - If the inheritance is
protected
, onlyChild
, and its children, are aware that they inherit fromBase
. - If the inheritance is
private
, no one other thanChild
is aware of the inheritance.
Solution 2
class A
{
public:
int x;
protected:
int y;
private:
int z;
};
class B : public A
{
// x is public
// y is protected
// z is not accessible from B
};
class C : protected A
{
// x is protected
// y is protected
// z is not accessible from C
};
class D : private A // 'private' is default for classes
{
// x is private
// y is private
// z is not accessible from D
};
IMPORTANT NOTE: Classes B, C and D all contain the variables x, y and z. It is just question of access.
About usage of protected and private inheritance you could read here.
Solution 3
Limiting the visibility of inheritance will make code not able to see that some class inherits another class: Implicit conversions from the derived to the base won't work, and static_cast
from the base to the derived won't work either.
Only members/friends of a class can see private inheritance, and only members/friends and derived classes can see protected inheritance.
public inheritance
-
IS-A inheritance. A button is-a window, and anywhere where a window is needed, a button can be passed too.
class button : public window { };
protected inheritance
-
Protected implemented-in-terms-of. Rarely useful. Used in
boost::compressed_pair
to derive from empty classes and save memory using empty base class optimization (example below doesn't use template to keep being at the point):struct empty_pair_impl : protected empty_class_1 { non_empty_class_2 second; }; struct pair : private empty_pair_impl { non_empty_class_2 &second() { return this->second; } empty_class_1 &first() { return *this; // notice we return *this! } };
private inheritance
-
Implemented-in-terms-of. The usage of the base class is only for implementing the derived class. Useful with traits and if size matters (empty traits that only contain functions will make use of the empty base class optimization). Often containment is the better solution, though. The size for strings is critical, so it's an often seen usage here
template<typename StorageModel> struct string : private StorageModel { public: void realloc() { // uses inherited function StorageModel::realloc(); } };
public member
-
Aggregate
class pair { public: First first; Second second; };
-
Accessors
class window { public: int getWidth() const; };
protected member
-
Providing enhanced access for derived classes
class stack { protected: vector<element> c; }; class window { protected: void registerClass(window_descriptor w); };
private member
-
Keep implementation details
class window { private: int width; };
Note that C-style casts purposely allows casting a derived class to a protected or private base class in a defined and safe manner and to cast into the other direction too. This should be avoided at all costs, because it can make code dependent on implementation details - but if necessary, you can make use of this technique.
Solution 4
These three keywords are also used in a completely different context to specify the visibility inheritance model.
This table gathers all of the possible combinations of the component declaration and inheritance model presenting the resulting access to the components when the subclass is completely defined.
The table above is interpreted in the following way (take a look at the first row):
if a component is declared as public and its class is inherited as public the resulting access is public.
An example:
class Super {
public: int p;
private: int q;
protected: int r;
};
class Sub : private Super {};
class Subsub : public Sub {};
The resulting access for variables p
, q
, r
in class Subsub is none.
Another example:
class Super {
private: int x;
protected: int y;
public: int z;
};
class Sub : protected Super {};
The resulting access for variables y
, z
in class Sub is protected and for variable x
is none.
A more detailed example:
class Super {
private:
int storage;
public:
void put(int val) { storage = val; }
int get(void) { return storage; }
};
int main(void) {
Super object;
object.put(100);
object.put(object.get());
cout << object.get() << endl;
return 0;
}
Now lets define a subclass:
class Sub : Super { };
int main(void) {
Sub object;
object.put(100);
object.put(object.get());
cout << object.get() << endl;
return 0;
}
The defined class named Sub which is a subclass of class named Super
or that Sub
class is derived from the Super
class.
The Sub
class introduces neither new variables nor new functions. Does it mean that any object of the Sub
class inherits all the traits after the Super
class being in fact a copy of a Super
class’ objects?
No. It doesn’t.
If we compile the following code, we will get nothing but compilation errors saying that put
and get
methods are inaccessible. Why?
When we omit the visibility specifier, the compiler assumes that we are going to apply the so-called private inheritance. It means that all public superclass components turn into private access, private superclass components won't be accessible at all. It consequently means that you are not allowed to use the latter inside the subclass.
We have to inform the compiler that we want to preserve the previously used access policy.
class Sub : public Super { };
Don’t be misled: it doesn’t mean that private components of the Super class (like the storage variable) will turn into public ones in a somewhat magical way. Private components will remain private, public will remain public.
Objects of the Sub
class may do "almost" the same things as their older siblings created from the Super
class. "Almost" because the fact of being a subclass also means that the class lost access to the private components of the superclass. We cannot write a member function of the Sub
class which would be able to directly manipulate the storage variable.
This is a very serious restriction. Is there any workaround?
Yes.
The third access level is called protected. The keyword protected means that the component marked with it behaves like a public one when used by any of the subclasses and looks like a private one to the rest of the world. -- This is true only for the publicly inherited classes (like the Super class in our example) --
class Super {
protected:
int storage;
public:
void put(int val) { storage = val; }
int get(void) { return storage; }
};
class Sub : public Super {
public:
void print(void) {cout << "storage = " << storage;}
};
int main(void) {
Sub object;
object.put(100);
object.put(object.get() + 1);
object.print();
return 0;
}
As you see in the example code we a new functionality to the Sub
class and it does one important thing: it accesses the storage variable from the Super class.
It wouldn’t be possible if the variable was declared as private. In the main function scope the variable remains hidden anyway so if you write anything like:
object.storage = 0;
The compiler will inform you that it is an error: 'int Super::storage' is protected
.
Finally, the last program will produce the following output:
storage = 101
Solution 5
It has to do with how the public members of the base class are exposed from the derived class.
- public -> base class's public members will be public (usually the default)
- protected -> base class's public members will be protected
- private -> base class's public members will be private
As litb points out, public inheritance is traditional inheritance that you'll see in most programming languages. That is it models an "IS-A" relationship. Private inheritance, something AFAIK peculiar to C++, is an "IMPLEMENTED IN TERMS OF" relationship. That is you want to use the public interface in the derived class, but don't want the user of the derived class to have access to that interface. Many argue that in this case you should aggregate the base class, that is instead of having the base class as a private base, make in a member of derived in order to reuse base class's functionality.
Comments
-
nbro about 2 years
What is the difference between
public
,private
, andprotected
inheritance in C++?All of the questions I've found on SO deal with specific cases.
-
Johannes Schaub - litb about 15 yearsBetter say "public: the inheritance will be seen by everyone". protected: the inheritance will only be seen by derived classes and friends", "private: the inheritance will only be seen by the class itself and friends". This is different from your wording, since not only the members can be invisible, but also the IS-A relation can be invisible.
-
Rich about 14 yearsThe one time I used private inheritance was to do Just what Doug T describes i.e "you want to use the public interface in the derived class, but don't want the user of the derived class to have access to that interface". I basically used it to seal off the old interface and expose another one through the derived class.
-
DangerMouse almost 12 yearsI think Scott Myers (as much as I like his stuff) has a lot to answer for the general confusion. I now think his analogies of IS-A and IS-IMPLEMENTED-IN-TERMS-OF are in sufficient for what is going on.
-
Sam Kauffman about 11 yearsThis misleading. Private members of a base class behave quite differently from ordinary private class members--they're not accessible from the derived class at all. I think your column of three "Private" should be a column of "Inaccessible". See Kirill V. Lyadvinsky's answer to this question.
-
user4951 over 10 yearsSome says an as a relationship. Like using chair as a hammer. Here chair : protected hammer
-
Iwillnotexist Idonotexist over 9 yearsWhat Anzurio wrote only clicked in conjunction with your answer immediately below. Плус 1.
-
Zhe Chen about 9 yearsI'd like to add a few words that visibility in C++ is based on class instead of on object, which means that objects of the same class can access each other's private fields without restriction.
-
Zelldon about 9 yearsJava has only public inheritance
-
Enissay about 9 yearsThis not the topic to speak about java but NO, you're wrong... Follow the link in my answer above for details
-
Zelldon about 9 yearsYou mentioned java so it is the topic. And your example handles the specifiers which use in jaca. The question is about the specifiers for inheritance which are not exist in Java an made a difference. If a field in superclass is public and the inheritance is private the field is only acessible inside the subclass. Outside there is no indication if the subclass extends the superclass. But your table explains only the specifiers for field and methods.
-
The Vivandiere about 9 yearsIf you have a hard time understanding this, read Kirill V. Lyadvinsky's answer, then come back and read this.
-
Destructor over 8 yearswhen using containment instead of private inheritance is not as convenient as private inheritance? Will you please explain it using an example?
-
sbi over 8 years@Pravasi: If
D
derives privately fromD
, it can override virtual functions ofB
. (If, for example,B
is an observer interface, thenD
could implement it and passthis
to functions requiring auch an interface, without everybody being able to useD
as an observer.) Also,D
could selectively make members ofB
available in its interface by doingusing B::member
. Both is syntactically inconvenient to implement whenB
is a member. -
underscore_d over 8 yearsThis is just another case that illustrates how, for the most part, inheriting from
SomeBase
is just like a hardcoded way to compose-in an anonymous member of typeSomeBase
. This, like any other member, has an access specifier, which exerts the same control on external access. -
gen almost 8 years@ZheChen if I have objects Tom and Jerry of class Person with private field age how do you access (and modify?) Jerry's age using Tom?
-
JobHunter69 over 7 yearsOn the last line, do you mean "no one other than 'Base' is aware of the inheritance?"
-
Water over 7 yearsFirst one to mention the lack of a modifier (as in Class : SuperClass) yields private. This is an important piece the others are missing out, along with thorough explanations. +1
-
lorro about 7 years@sbi: old one but... containment is a no-go in case of CRTP and/or virtuals (as you correctly described in comment - but that means that it can't be modelled as containment if B has abstract methods and you're not allowed to touch it).
protected
inheritance I've found useful with avirtual
base class andprotected
ctor:struct CommonStuff { CommonStuff(Stuff*) {/* assert !=0 */ } }; struct HandlerMixin1 : protected virtual CommonStuff { protected: HandlerMixin1() : CommonStuff(nullptr) {} /*...*/ }; struct Handler : HandlerMixin1, ... { Handler(Stuff& stuff) : CommonStuff(&stuff) {} };
-
lorro about 7 years@ZheChen: The part after 'which means that' is true. The part before is only true for
private
.protected
declarations of the base class are accessible to the descendant object, not class:struct Base { protected: int x; }; struct Desc : Base { Desc(Base& b) { (void)b.x; } };
fails to compile. -
KRoy about 7 yearsI want to add one fundamental difference that in Java all the abstract methods have to be declared public in the derived class/subclass. But in C++ private inheritance the methods can be declared private. Suppose class Controller : private ActionListener { private: int onAction() {...} void setup() {registerActionListener(*this);}} Here (*this) is passed as ActionListener object even if they are declared private.
-
Assimilater almost 7 yearsJava is dumb that way
-
tjwrona1992 almost 7 yearsMy understanding of how this worked was SO FAR OFF! Thank you so much for clarifying.
-
cp.engr almost 6 yearsOverkill IMO, but I like the table at the beginning.
-
lineil almost 6 yearsCould you illustrate what you mean by "aware of the 'inheritance'"? I can understand "i can access this I cannot access that" but i don't get it when one says "I know that A inherits from B" what am I doing here am I checking the inheritance?
-
curiousguy over 5 years@underscore_d "an anonymous member of type SomeBase" it isn't anonymous: it's called
Derived::SomeBase
-
Chan Kim about 5 yearsit took me some time to understand this. But now it's clear. Thanks!
-
John Leuenhagen almost 4 yearsGood note about 'private' being the default for classes.
-
Milan almost 4 years@ZheChen, if I have two objects:
b1
andb2
of the same classBase
then how canb1
access (as well as modify) the private members ofb2
? -
Zhe Chen almost 4 years@Milan It's possible. That can be proved by a simple snippet: repl.it/repls/CarefulPinkSpof#main.cpp
-
Milan almost 4 years@ZheChen thanks a lot for sharing the code snippet. I had never thought about this. As now I got your point, just curious, in what type of situations/applications this concept is used? If you have any practical example(s) then please do share. It would help a lot in understanding this concept even better.
-
Soup Endless over 3 yearsThat's actually the best way to explain inheritance access I'd ever seen.
-
Badhan Sen about 3 yearsMarvelous explanation. So far everything is clear here.
-
Wade Wang almost 3 yearsThere is a more detailed explanation with examples : programiz.com/cpp-programming/….
-
DavidW over 2 years@Milan Move constructor is probably the most common example of something that access and modifies members of another instance. Copy constructor and comparison operators would often access (but not modify) member of another instance.