18 / 163 · C++11 · 8 min
Access Control and Inheritance Privileges
public, protected, and private control the accessibility of names; the inheritance specifier also controls whether the base-class interface and upcasts are available to outsiders. Access checking does not filter overload candidates for you, and protected does not let a derived class access protected members through an arbitrary base-class object.
In this lesson
Member access and type defaults
public is for ordinary callers, private allows access only by the class that declared the member and its friends, and protected additionally opens restricted access to derived classes and their friends. A class's members can access private members of other objects of the same class, so access is judged by class and use context, not by each object having a separate access list.
class defaults both member access and the default inheritance specifier to private; struct defaults both to public. Either can define constructors, templates, and virtual functions. friend is an explicit grant; it is not automatically transitive, inherited, or bidirectional, and it is suitable for a small number of functions that truly need tight collaboration.
The inheritance specifier changes external visibility
Public inheritance preserves the corresponding accessibility of the base class's public and protected members; protected inheritance makes those members accessible as protected in the derived class; private inheritance makes them accessible as private. The base class's original private members still exist in the base subobject, but the derived class cannot name them directly.
Public, unambiguous inheritance allows outsiders to implicitly convert a derived-class pointer to a base-class pointer; private inheritance does not allow ordinary external callers to perform that conversion. The example uses type traits to show that both classes have a base-class relationship, yet not both can convert externally—that is the difference between implementation reuse and a public subtype.
protected and lookup order
When a derived-class member accesses a protected non-static member of the base class, the object expression usually must have the type of that derived class or a class further derived from it. A derived class can modify a protected field of another object of the same derived class, but it cannot treat an arbitrary Base& as a back door to all base-class objects.
Name lookup and overload resolution first determine the candidates and the best match, then access checking runs. If the best match is private, the compiler reports an error; it does not fall back to a worse-matching public overload. When diagnosing an access error, first see which declaration was actually selected, then check whether the current context is allowed to use it.
Pitfalls
- Private inheritance does not delete base-class members; it only restricts the external interface and conversions. Do not infer object layout from that.
- A private virtual function can still be overridden by a derived class; the override relationship and access checking of a call expression are two different things.
Run an example
Minimum C++11 · complete program · Download .cpp
#include <cassert>
#include <type_traits>
class Base {
int secret_ = 7;
protected:
int count_ = 0;
public:
int secret() const { return secret_; }
};
class PublicChild : public Base {
public:
void set_peer(PublicChild& peer, int value) { peer.count_ = value; }
int count() const { return count_; }
};
class PrivateChild : private Base {
public:
int exposed_secret() const { return secret(); }
};
int main() {
static_assert(std::is_base_of<Base, PrivateChild>::value, "base exists");
static_assert(std::is_convertible<PublicChild*, Base*>::value, "public");
static_assert(!std::is_convertible<PrivateChild*, Base*>::value, "private");
PublicChild first, second;
first.set_peer(second, 9);
assert(second.count() == 9);
PrivateChild hidden;
assert(hidden.exposed_secret() == 7);
}
Compile locally
g++ -std=c++11 -Wall -Wextra -Wpedantic -pthread objects-access.cpp -o example && ./exampleExpected result
Expected: exit 0, no output; every assert holds.
CHECK YOUR UNDERSTANDING
Close the answer. Explain it.
Is writing void inspect(Base& b) { b.count_ = 1; } inside PublicChild legal? How should it be fixed?
Show a reference answer
It is not legal. Although the function belongs to the derived class, b's static type is Base, which does not satisfy the restriction on accessing a protected object here. If the operation is meant only for objects of the same derived type, change the parameter to PublicChild&. If modifying an arbitrary Base is truly allowed, Base should provide a public operation that maintains its own invariants, rather than using a cast to bypass the interface.
Check the sources
Drafts and official chapters change. The version mark is only the example’s minimum.