访问指定符

来自cppreference.com
< cpp‎ | language

class/structunionmember-specification 中定义后继成员的可访问性。

派生类声明的 base-specifier 中,定义继承自后继基类的成员的可访问性。

目录

[编辑] 语法

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) 指定符后的成员拥有公开成员访问
2) 指定符后的成员拥有受保护成员访问
3) 指定符后的成员拥有私有成员访问
4) 公开继承:列于指定符后的基类的公开和受保护成员在派生类中保持其成员访问
5) 受保护继承:列于指定符后的基类的公开和受保护成员在派生类中是受保护成员
6) 私有继承:列于指定符后的基类的公开和受保护成员在派生类中是私有成员

[编辑] 解释

每个成员(静态、非静态、函数、类型等)拥有关联的“成员访问”。在程序的任何位置使用成员的名称时,检查其访问,而且若它不满足访问规则,则程序不能编译:

#include <iostream>
class Example {
 public: // 此点后所有声明为公开
    void add(int x) { // 成员 "add" 拥有公开访问
        n += x; // OK :受保护的 Example::n 能从 Example::add 访问
    }
 private: // 此点后所有声明为私有
    int n = 0; // 成员 "n" 拥有私有访问
};
int main()
{
    Example e;
    e.add(1); // OK :公开的 Example::add 能从 main 访问
//  e.n = 7;  // 错误:私有的 Example::n 不能从 main 访问
}


访问指定符给予类作者决定哪些类成员能为类的用户所访问(接口),而哪些成员用于内部使用(实现)。

[编辑] 细节

类的所有成员(成员函数体、成员对象的初始化器及整个嵌套类定义)拥有对类所能访问的所有名称的访问。成员函数内的局部类拥有对成员函数自身所能访问的所有名称的访问。

以关键词 class 定义的类默认拥有其成员和其基类的私有访问。以关键词 struct 定义的类默认拥有对其成员和其基类的公开访问。 union 默认拥有对其成员的公开访问。

为授予另外的函数或类对受保护或私有成员的访问,可使用友元声明

可访问性应用到所有名称,而不考虑其起源,故检查的是以 typedefusing 声明引入的名称,而非其指涉的名称。

class A : X {
  class B { }; // B 在 A 中私有
public:
  typedef B BB; // BB 公开
};
void f() {
  A::B y; // 错误: A::B 私有
  A::BB x; // OK : A::BB 公开
}

成员访问不影响可见性:私有和私有继承的成员为重载决议可见并考虑,仍然考虑到不可访问基类的隐式转换等。成员访问检查是转译任何给定语言构造后的最后一步。此规则的目的是以 public 替换任何 private 决不改变程序的行为。

用于默认函数参数还有默认模板形参中名称的访问检查在声明点进行,而非在使用点。

虚函数名的访问规则在调用点,使用用于指代调用该成员函数的对象的表达式类型检查。忽略最终派生类。

struct B { virtual int f(); }; // f 在 B 中公开
class D : public B { private: int f(); }; // f 在 D 中私有
void f() {
 D d;
 B& b = d;
 b.f(); // OK : B::f() 公开,调用 D::f() ,即使它为私有
 d.f(); // 错误: D::f() 为私有
}

按照非限定名称查找为私有的名称,可以通过限定名称查找访问:

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

可通过继承图中多条路径访问的名称拥有带最多访问的路径的访问:

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 可通过 B 访问 C
};

类中可出现任何数量的访问指定符,以任何顺序。成员访问指定符可能影响类布局:非静态数据成员的地址只保证对于拥有相同访问的成员以声明顺序增加。对于标准布局类型 (StandardLayoutType) ,所有非静态数据成员必须拥有相同访问。

在类中重声明成员时,必须在相同成员访问下进行:

struct S {
  class A; // S::A 公开
private:
  class A {}; // 错误:不能更改访问
};

[编辑] 公开成员访问

公开成员组成类公开接口的一部分(公开接口的另外部分是 ADL 所找到的非成员函数)。

类的公开成员可在任何位置访问。

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

[编辑] 受保护成员访问

受保护成员访问组成派生类的接口(与类的公开接口有别)。

Base 的受保护成员只能为下列者所访问

1) Base 的成员和友元
2) 任何派生自 Base 的类的成员和友元 (C++17 前),但仅在操作从 Base 派生的类型对象(包含 this )时
struct Base {
 protected:
    int i;
 private:
    void g(Base& b, struct Derived& d);
};
 
struct Derived : Base {
    void f(Base& b, Derived& d) // 派生类的成员函数
    {
        ++d.i; // OK : d 的类型是 Derived
        ++i; // OK :隐含的 '*this' 的类型为 Derived
//      ++b.i; // 错误:不能通过 Base 访问受保护成员
    }
};
 
void Base::g(Base& b, Derived& d) // Base 的成员函数
{
    ++i; // OK
    ++b.i; // OK
    ++d.i; // OK
}
 
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; // OK
    }
};

[编辑] 私有成员访问

私有成员组成类的实现,还有来自类的其他成员的私有接口。

类的私有成员只能为类的成员和友元所访问,无关乎成员在同一还是不同实例:

class S {
 private:
    int n; // S::n 私有
 public:
    S() : n(10) {} // this->n 可访问于 S::S
    S(const S& other) : n(other.n) {} // other.n 可访问于 in S::S
};

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

[编辑] 继承

公开、受保护和私有继承的含义,见派生类