Namespaces
Variants

Access specifiers

From zh.cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
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
Special member functions
Templates
Miscellaneous

在 类/结构体 或 联合体 的 成员声明序列 中,定义后续成员的可访问性。

在 派生类 声明的 基类说明符 中,定义后续基类继承成员的访问权限。

目录

语法

public : 成员声明 (1)
protected : 成员声明 (2)
private : 成员声明 (3)
public 基类 (4)
protected 基类 (5)
private 基类 (6)
1) 在访问说明符之后声明的成员具有公共成员访问权限。
2) 访问说明符后声明的成员具有受保护成员访问权限。
3) 访问说明符之后声明的成员具有私有成员访问权限。
4) 公有继承 :在访问说明符后列出的 基类 的公有成员和受保护成员,在派生类中保持其原有的成员访问权限。
5) 保护继承 :访问说明符后列出的 基类 的公开成员和保护成员将成为派生类的保护成员。
6) 私有继承 :访问说明符后列出的 基类 的公开和受保护成员将成为派生类的私有成员。

基类的私有成员始终对派生类不可访问,无论采用公有、受保护还是私有继承方式。

说明

每个 类 成员(静态、非静态、函数、类型等)的名称都具有关联的“成员访问权限”。当程序在任何地方使用成员名称时,会检查其访问权限,如果不满足访问规则,则程序无法编译:

#include <iostream>
class Example
{
public:             // 此后的所有声明均为公开
    void add(int x) // 成员函数“add”具有公开访问权限
    {
        n += x;     // 正确:私有成员 Example::n 可在 Example::add 中访问
    }
private:            // 此后的所有声明均为私有
    int n = 0;      // 成员“n”具有私有访问权限
};
int main()
{
    Example e;
    e.add(1); // 正确:公开成员函数 Example::add 可在 main 中访问
//  e.n = 7;  // 错误:私有成员 Example::n 不能在 main 中访问
}

访问说明符赋予类作者决定哪些类成员可供类的使用者访问(即 接口 ),哪些成员仅用于类的内部实现(即 实现 )的能力。

详细说明

类的所有成员声明(包括在类外定义的)都可以访问该类所能访问的所有名称。成员函数内的局部类可以访问该成员函数所能访问的所有名称。

使用关键字 class 定义的类默认对其成员和基类具有私有访问权限。使用关键字 struct 定义的类默认对其成员和基类具有公有访问权限。联合体默认对其成员具有公有访问权限。

为了授予其他函数或类访问受保护或私有成员的权限,可以使用 友元声明。

可访问性适用于所有名称,无论其来源如何,因此由 typedef 或 using 声明(继承构造函数除外)引入的名称会被检查,而不是它所指的名称:

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
}

成员访问不影响可见性:私有成员和私有继承的成员名称是可见的,并且会被重载解析考虑,到不可访问基类的隐式转换仍然会被考虑,等等。成员访问检查是在任何给定语言结构被解释后的最后一步。这条规则的意图是,将任何 private 替换为 public 永远不会改变程序的行为。

对于 默认函数实参以及默认模板参数中使用的名称的访问检查在声明点执行,而不是在使用点。

对于虚函数名称的访问规则在调用点使用用于表示被调用成员函数对象的表达式类型进行检查。最终覆盖器的访问被忽略:

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
}

根据非限定名字查找是私有的名称,可能通过限定名字查找可访问:

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
};

在继承图中可通过多条路径访问的名称具有访问权限最大的那条路径的访问权限:

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
    }
};

类中可出现任意数量的访问说明符,且顺序任意。

成员访问说明符可能影响类布局:非静态数据成员的地址仅保证对于未被访问说明符分隔的(C++11 前)具有相同访问权限的(C++11 起)成员按声明顺序递增。

(C++23 前)

对于标准布局类型,所有非静态数据成员必须具有相同的访问权限。

(C++11 起)

当成员在同一类中被重新声明时,它必须在相同的成员访问权限下进行:

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

公有成员访问

公共成员构成类公共接口的一部分(公共接口的其他部分包括通过 ADL 查找到的非成员函数)。

类的公共成员可在任意位置访问:

class S
{
public:
    // n、E、A、B、C、U、f 是公有成员
    int n;
    enum E {A, B, C};
    struct U {};
    static void f() {}
};
int main()
{
    S::f();     // S::f 可在 main 中访问
    S s;
    s.n = S::B; // S::n 和 S::B 可在 main 中访问
    S::U x;     // S::U 可在 main 中访问
}

受保护成员访问

受保护成员构成了类对其派生类的接口(这与类的公共接口有所区别)。

类的受保护成员仅可被以下对象访问

1) 对该类的成员和友元;
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) // 派生类的成员函数
    {
        ++d.i;                  // 正确:d的类型是Derived
        ++i;                    // 正确:隐式'*this'的类型是Derived
//      ++b.i;                  // 错误:不能通过Base访问受保护成员
                                // (否则将可能更改其他派生类,比如假设的
                                // Derived2的基类实现)
    }
};
void Base::g(Base& b, Derived& d) // Base的成员函数
{
    ++i;                          // 正确
    ++b.i;                        // 正确
    ++d.i;                        // 正确
}
void h(Base& b, Derived& d) // Derived的友元函数
{
    ++d.i;                  // 正确:Derived的友元可以通过Derived对象
                            // 访问受保护成员
//  ++b.i;                  // 错误:Derived的友元不是Base的友元
}
void x(Base& b, Derived& d) // 非成员非友元函数
{
//  ++b.i;                  // 错误:非成员函数无访问权限
//  ++d.i;                  // 错误:非成员函数无访问权限
}

当形成指向受保护成员的指针时,其声明必须使用派生类:

struct Base
{
protected:
    int i;
};
struct Derived : Base
{
    void f()
    {
//      int Base::* ptr = &Base::i;    // 错误:必须使用 Derived 进行命名
        int Base::* ptr = &Derived::i; // 正确
    }
};

私有成员访问

私有成员构成了类的实现,同时也为类的其他成员提供了私有接口。

类的私有成员仅对该类的成员和友元可访问,无论这些成员是否位于相同或不同的实例:

class S
{
private:
    int n; // S::n 是私有成员
public:
    S() : n(10) {}                    // 在 S::S 中可访问 this->n
    S(const S& other) : n(other.n) {} // 在 S::S 中可访问 other.n
};

显式转换 (C风格和函数风格)允许从派生类左值转换为其私有基类的引用,或从派生类指针转换为其私有基类的指针。

继承

参见 派生类 了解public、protected和private继承的含义。

关键词

public , protected , private

缺陷报告

下列行为变更缺陷报告被追溯应用于先前发布的C++标准。

缺陷报告 应用于 发布时的行为 正确行为
CWG 1873 C++98 派生类的友元可以访问 protected 成员 改为不可访问