这是关于成员访问规则的正确摘要吗

Is this a correct summary on member access rules?

本文关键字:规则 访问 于成员 成员      更新时间:2023-10-16

我正在努力完全理解C++标准[class.access]部分的多个段落中定义的成员访问规则。它们非常复杂,甚至令人困惑,因此我需要一个简短但准确而详尽的摘要。

我编译这个程序是为了测试受保护成员在几种情况下的可访问性(因为受保护成员的规则最复杂):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.address/base-5.4]),3和该类的派生类的成员(参见[class.access/base-5.3]),如果这些受保护的成员既不是非静态数据成员也不是非静态成员函数(参见[class.access/class.prected-1])

我的总结正确吗?


1我在C++17中使用了Clang 9.0.0编译器。

2对类B的成员i的访问可以是直接的,也就是通过该类:b.i(直接访问),也可以是间接的,也可以通过该类的派生类Dd.i(heritage访问)。由于派生类继承的成员是其可访问性发生变化的派生类的成员(参见[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中重写了iIjJ,编译器会通过在X::fg中引发d.id.Id.jd.J访问的错误来完成这一操作)

如果您的问题是基于访问的,那么这些是c++中的规则。我将在下面做一个基本的总结,但要得到详尽的解释,请点击此处。这将更详细地介绍每种方法的工作原理。

public
类的公共成员可以在的任何位置访问

受保护
1。致该班的成员和朋友
2。到该类的任何派生类的成员和朋友(直到C++17),但只有当访问受保护成员的对象的类是该派生类或该派生类的派生类时

private
一个类的私有成员只能由该类的成员和朋友访问,无论这些成员是在同一个实例上还是在不同的实例上

要查看示例,请转到上面的链接。

有了嵌套类,您就在基类的范围内,所以私有和受保护的成员都可以访问。如果成员是静态的,则可以直接访问,否则必须构造该类的对象才能访问该类中的那些成员。下面是上面class X的一个例子:

class X: public B {
public:
class A {
public:
void b() {
std::cout << J << std::endl;
std::cout << S << std::endl;
}
void d(X x) {
std::cout << x.j << std::endl;
std::cout << x.s << std::endl;
}
};
void f();
protected:
int j = 2;
static int const J = 2;
private:
friend void g();
int s = 3;
static int const S = 4;
};

以下是公共、受保护和私有在使用它们进行继承时的含义

public
当一个类使用公共成员访问说明符从基派生时,基类的所有公共成员都可以作为派生类的公共成员访问,并且基类的所有受保护成员都可以用作派生类的受保护成员访问(除非是好友,否则永远无法访问基的私有成员)

protected
当一个类使用受保护成员访问说明符从基派生时,基类的所有公共和受保护成员都可以作为派生类的受保护成员进行访问(除非成为好友,否则永远无法访问基的私有成员)
private
当类使用私有成员访问说明符从基派生时,基类的所有公共成员和受保护成员都可以作为派生类的私有成员访问(除非是friended,否则永远无法访问基类的私有成员)。

注意:派生类继承基类的所有方法,但以下情况除外。

基类的构造函数、析构函数和复制构造函数
  • 重载运算符,如基类——这些运算符可能不会像您预期的那样起作用,并且应该以您为每个类的每个运算符重写的方式来实现。

  • 基类的友元函数。

  • 现在关于友元说明符,这来自这里的cpp引用在这里你将有关于如何使用它的例子和详细解释

    当涉及到标准库时,您还可以在那里找到大量信息的示例,您还将能够看到标准将来会出现什么,以及编译器支持什么功能。

    您错过了一个不那么直观的访问,c++依赖于编译,其中一个影响是的所有实例都是"相同的";并且如果该类上的函数可以接受"的引用,则可以访问彼此的私有变量;"他自己";,有关更多详细信息,请查看类变量CCD_ 20。

    #include <iostream>
    class MyClass{
    int num = 69;
    public:
    void set_num(int n){
    num = n;
    };
    int get_num() {
    return num;
    }
    int get_num_from(MyClass * ptr_ref) {
    return ptr_ref->num;
    }
    int get_num_from(MyClass ref) {
    return ref.num;
    }
    };
    
    int main(){
    MyClass class_a;
    MyClass class_b;
    class_a.set_num(0);
    std::cout << "class_a -> " << class_a.get_num() << std::endl;
    std::cout << "class_b ref -> " << class_b.get_num_from(class_a) << std::endl;
    std::cout << "class_b ptr_ref -> " << class_b.get_num_from(&class_a) << std::endl;
    }
    

    输出:

    class_a -> 0
    class_b ref -> 0
    class_b ptr_ref -> 0