Namespaces
Variants

Access specifiers

From 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 在 A 中是私有成员
public:
    typedef B BB; // BB 是公开成员
};
void f()
{
    A::B y;  // 错误:A::B 是私有成员
    A::BB x; // 正确:A::BB 是公开成员
}

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

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

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

struct B
{
    virtual int f(); // f在B中为public
};
class D : public B
{
private:
    int f(); // f在D中为private
};
void f()
{
    D d;
    B& b = d;
    b.f(); // 正确:B::f为public,即使D::f为private仍会调用D::f
    d.f(); // 错误:D::f为private
}

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

class A {};
class B : private A {};
class C : public B
{
    A* p;   // 错误:非限定名称查找找到的是B的私有基类A
    ::A* q; // 正确:限定名称查找找到的是命名空间级别的声明
};

在继承图中可通过多条路径访问的名称,具有最高访问权限路径的访问级别:

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(); // 正确:通过B类,W对C是可访问的
    }
};

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

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

(直到 C++23)

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

(since C++11)

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

struct S
{
    class A;    // S::A 是公开的
private:
    class A {}; // 错误:无法更改访问权限
};

公有成员访问

公共成员构成类公共接口的一部分(公共接口的其他部分包括通过 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 成员 改为不可访问