【发布时间】:2020-05-29 16:01:24
【问题描述】:
我试图完全理解 C++ 标准的 [class.access] 部分的多个段落中定义的成员访问规则。他们相当 complex or even confusing 因此我需要一个简短但准确而详尽的总结。
我编译了这个程序来测试几种情况下受保护成员的可访问性(因为受保护成员的规则是最复杂的):1
#include <iostream>
class B {
protected:
int i = 1;
static int const I = 1;
};
class X: public B {
protected:
int j = 2;
static int const J = 2;
public:
void f();
friend void g();
};
class D: public X {
protected:
int k = 3;
static int const K = 3;
};
void X::f() {
B b;
X x;
D d;
//std::cout << b.i; // error: 'i' is a protected member of 'B'
std::cout << b.I;
std::cout << x.i;
std::cout << x.I;
std::cout << x.j;
std::cout << x.J;
std::cout << d.i;
std::cout << d.I;
std::cout << d.j;
std::cout << d.J;
//std::cout << d.k; // error: 'k' is a protected member of 'D'
//std::cout << d.K; // error: 'K' is a protected member of 'D'
}
void g() {
B b;
X x;
D d;
//std::cout << b.i; // error: 'i' is a protected member of 'B'
//std::cout << b.I; // error: 'I' is a protected member of 'B'
std::cout << x.i;
std::cout << x.I;
std::cout << x.j;
std::cout << x.J;
std::cout << d.i;
std::cout << d.I;
std::cout << d.j;
std::cout << d.J;
//std::cout << d.k; // error: 'k' is a protected member of 'D'
//std::cout << d.K; // error: 'K' is a protected member of 'D'
}
int main() {
B b;
X x;
D d;
//std::cout << b.i; // error: 'i' is a protected member of 'B'
//std::cout << b.I; // error: 'I' is a protected member of 'B'
//std::cout << x.i; // error: 'i' is a protected member of 'B'
//std::cout << x.I; // error: 'I' is a protected member of 'B'
//std::cout << x.j; // error: 'j' is a protected member of 'X'
//std::cout << x.J; // error: 'J' is a protected member of 'X'
//std::cout << d.i; // error: 'i' is a protected member of 'B'
//std::cout << d.I; // error: 'I' is a protected member of 'B'
//std::cout << d.j; // error: 'j' is a protected member of 'X'
//std::cout << d.J; // error: 'J' is a protected member of 'X'
//std::cout << d.k; // error: 'k' is a protected member of 'D'
//std::cout << d.K; // error: 'K' is a protected member of 'D'
return 0;
}
我对直接可访问性得出了这个结论:2
- 任何实体都可以直接访问类的公共成员(参见[class.access/base-5.1]);
- 类的私有成员只能由该类的成员和朋友直接访问(参见[class.access/base-5.2]);
- 类的受保护成员只能由该类的成员和朋友直接访问(参见[class.access/base-5.3]),如果满足以下条件,该类的基类的成员和朋友可以直接访问这些受保护的成员继承自这些基类或这些基类的基类(参见[class.access/base-5.4]),3 以及该类的派生类的成员(参见[class.access/base-5.3])如果这些受保护成员不是非静态数据成员也不是非静态成员函数(参见[class.access/class.protected-1])。
我的总结正确吗?
1 我在 C++ 17 中使用了 Clang 9.0.0 编译器。
2 对B 类的成员i 的访问可以是直接的,即通过该类:b.i(直接访问),或间接的,即通过该类的派生类D:d.i(继承访问)。由于派生类继承的成员是该派生类的成员,其可访问性发生了变化(参见[class.access/base-1]),因此对类成员的继承访问可以被视为对该派生类的继承成员的直接访问班级。换句话说,只需要考虑直接访问。
3我这里的条款与标准[class.access/base-5.4]的引用条款略有不同:
如果在类 N 中命名,成员 m 可在点 R 访问
- …
- …
- …
- 存在一个可在 R 访问的 N 的基类 B,当在类 B 中命名时,m 可在 R 访问。
这是因为编译器的行为不同,我的感觉是编译器是正确的。在我看来,该标准的条款存在两个问题:
- 访问点 R 应限制为 B 类的成员和朋友(这是编译器通过在程序中的
main中为d.*访问引发错误来执行的操作); - 类 N 中的成员 m 应该被限制为从类 B 继承,而不是被类 N 覆盖(这是编译器会通过引发
d.i、d.I、d.j、和d.J访问X::f和g,在程序中的D中有i、I、j和J被覆盖)。
【问题讨论】:
-
如果受保护的成员是静态的,那么和该类的基类的成员是什么意思。 基类没有对派生自它的事物的特殊访问权。
-
在这里你可以找到访问限定符的很好的总结stackoverflow.com/questions/860339/…
-
@NathanOliver 哎呀,我颠倒了一切。已更新,谢谢。
-
@TarekDakhran 我的帖子不是关于访问说明符的继承,这很简单。它是关于访问说明符的语义。
-
该标准旨在“准确和详尽”。诚然,它并不是很短,但这是试图既准确又详尽的直接结果。例如,您的摘要忽略了私有继承和受保护继承的影响。
标签: c++ language-lawyer protected access-specifier