Access specifiers

From cppreference.com
< cpp‎ | language
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
for
range-for (C++11)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications (until C++17*)
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Access specifiers
friend specifier

Class-specific function properties
explicit (C++11)
static

Special member functions
Templates
Miscellaneous

In a member-specification of a class/struct or union, define the accessibility of subsequent members.

In a base-specifier of a derived class declaration, define the accessibility of inherited members of the subsequent base class.

Syntax

public : member-declarations (1)
protected : member-declarations (2)
private : member-declarations (3)
public base-class (4)
protected base-class (5)
private base-class (6)
1) The members declared after the access specifier have public member access.
2) The members declared after the access specifier have protected member access.
3) The members declared after the access specifier have private member access.
4) Public inheritance: the public and protected members of the base class
5) Protected inheritance: the public and protected members of the base class
6) Private inheritance: the public and protected members of the base class

The private members of the base class are always inaccessible to the derived class regardless of public, protected, or private inheritance.

Explanation

The name of every class

#include <iostream>
 
class Example
{
public:             // all declarations after this point are public
    void add(int x) // member "add" has public access
    {
        n += x;     // OK: private Example::n can be accessed from Example::add
    }
private:            // all declarations after this point are private
    int n = 0;      // member "n" has private access
};
 
int main()
{
    Example e;
    e.add(1); // OK: public Example::add can be accessed from main
//  e.n = 7;  // error: private Example::n cannot be accessed from main
}

Access specifiers give the author of the class the ability to decide which class members are accessible to the users of the class (that is, the interface) and which members are for internal use of the class (the implementation

In detail

All members of a class (bodies of member functions, initializers of member objects, and the entire nested class definitions

A class defined with the keyword class has private access for its members and its base classes by default. A class defined with the keyword struct has public access for its members and its base classes by default. A union

To grant access to additional functions or classes to protected or private members, a friendship declaration may be used.

Accessibility applies to all names with no regard to their origin, so a name introduced by a typedef or using declarations

class A : X
{
    class B {};   // B is private in A
public:
    typedef B BB; // BB is public
};
 
void f()
{
    A::B y;  // error: A::B is private
    A::BB x; // OK: A::BB is public
}

Member access does not affect visibility: names of private and privately-inherited members are visible and considered by overload resolution, implicit conversions to inaccessible base classes are still considered, etc. Member access check is the last step after any given language construct is interpreted. The intent of this rule is that replacing any private with public

Access checking for the names used in default function arguments as well as in the default template parameters

Access rules for the names of virtual functions

struct B
{
    virtual int f(); // f is public in B
};
 
class D : public B
{
private:
    int f(); // f is private in D
};
 
void f()
{
    D d;
    B& b = d;
 
    b.f(); // OK: B::f is public, D::f is invoked even though it's private
    d.f(); // error: D::f is private
}

A name that is private according to unqualified name lookup, may be accessible through qualified name lookup:

class A {};
 
class B : private A {};
 
class C : public B
{
    A* p;   // error: unqualified name lookup finds A as the private base of B
    ::A* q; // OK: qualified name lookup finds the namespace-level declaration
};

A name that is accessible through multiple paths in the inheritance graph has the access of the path with the most access:

class W
{
public:
    void f();
};
 
class A : private virtual W {};
 
class B : public virtual W {};
 
class C : public A, public B
{
    void f()
    {
        W::f(); // OK: W is accessible to C through B
    }
};

Any number of access specifiers may appear within a class, in any order.

Member access specifiers may affect class layout: the addresses of non-static data members are only guaranteed to increase in order of declaration for the members not separated by an access specifier(until C++11) with the same access(since C++11)

(until C++23)

For standard-layout types, all non-static data members must have the same access.

(since C++11)

When a member is redeclared within the same class, it must do so under the same member access:

struct S
{
    class A;    // S::A is public
private:
    class A {}; // error: cannot change access
};

Public member access

Public members form a part of the public interface of a class (other parts of the public interface are the non-member functions found by ADL).

A public member of a class is accessible anywhere:

class S
{
public:
    // n, E, A, B, C, U, f are public members
    int n;
    enum E {A, B, C};
    struct U {};
    static void f() {}
};
 
int main()
{
    S::f();     // S::f is accessible in main
 
    S s;
    s.n = S::B; // S::n and S::B are accessible in main
 
    S::U x;     // S::U is accessible in main
}

Protected member access

Protected members form the interface of a class to its derived classes (which is distinct from the public interface of the class).

A protected member of a class is only accessible

1) to the members and friends of that class;
2)
struct Base
{
protected:
    int i;
private:
    void g(Base& b, struct Derived& d);
};
 
struct Derived : Base
{
    friend void h(Base& b, Derived& d);
    void f(Base& b, Derived& d) // member function of a derived class
    {
        ++d.i;                  // OK: the type of d is Derived
        ++i;                    // OK: the type of the implied '*this' is Derived
//      ++b.i;                  // error: can't access a protected member through
                                // Base (otherwise it would be possible to change
                                // other derived classes, like a hypothetical
                                // Derived2, base implementation)
    }
};
 
void Base::g(Base& b, Derived& d) // member function of Base
{
    ++i;                          // OK
    ++b.i;                        // OK
    ++d.i;                        // OK
}
 
void h(Base& b, Derived& d) // Friend of Derived
{
    ++d.i;                  // OK: friend of Derived can access a protected 
                            // member through an object of Derived
//  ++b.i;                  // error: friend of Derived is not a friend of Base
}
 
void x(Base& b, Derived& d) // non-member non-friend
{
//  ++b.i;                  // error: no access from non-member
//  ++d.i;                  // error: no access from non-member
}

When a pointer to a protected member is formed, it must use a derived class in its declaration:

struct Base
{
protected:
    int i;
};
 
struct Derived : Base
{
    void f()
    {
//      int Base::* ptr = &Base::i;    // error: must name using Derived
        int Base::* ptr = &Derived::i; // OK
    }
};

Private member access

Private members form the implementation of a class, as well as the private interface for the other members of the class.

A private member of a class is only accessible to the members and friends of that class, regardless of whether the members are on the same or different instances:

class S
{
private:
    int n; // S::n is private
public:
    S() : n(10) {}                    // this->n is accessible in S::S
    S(const S& other) : n(other.n) {} // other.n is accessible in S::S
};

The explicit cast

Inheritance

See derived classes for the meaning of public, protected, and private inheritance.

Keywords

public, protected, private

Defect reports

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

DR Applied to Behavior as published Correct behavior
CWG 1873 C++98 protected members were accessible to friends of derived classes made inaccessible