这是对成员访问规则的正确总结吗?

Posted

技术标签:

【中文标题】这是对成员访问规则的正确总结吗?【英文标题】:Is this a correct summary on member access rules? 【发布时间】: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 编译器。

2B 类的成员i 的访问可以是直接的,即通过该类:b.i直接访问),或间接的,即通过该类的派生类Dd.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.id.Id.j、和d.J 访问X::fg,在程序中的D 中有iIjJ 被覆盖)。

【问题讨论】:

如果受保护的成员是静态的,那么和该类的基类的成员是什么意思。 基类没有对派生自它的事物的特殊访问权。 在这里你可以找到访问限定符的很好的总结***.com/questions/860339/… @NathanOliver 哎呀,我颠倒了一切。已更新,谢谢。 @TarekDakhran 我的帖子不是关于访问说明符的继承,这很简单。它是关于访问说明符的语义 该标准旨在“准确和详尽”。诚然,它并不是很短,但这是试图既准确又详尽的直接结果。例如,您的摘要忽略了私有继承和受保护继承的影响。 【参考方案1】:

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

公开 类的公共成员可以在任何地方访问

受保护 1. 致该班的成员和朋友 2. 对该类的任何派生类的成员和朋友(C++17 前),但仅当访问受保护成员的对象的类是该派生类或该派生类的派生类

私人 一个类的私有成员只能被该类的成员和朋友访问,不管成员是在相同的还是不同的实例上

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

对于嵌套类,您在该基类的范围内,因此可以访问私有成员和受保护成员。如果成员是静态的,您将能够直接访问,否则必须构造该类的对象才能访问该类中的那些成员。这是上面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;
;

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

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

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

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

基类的构造函数、析构函数和复制构造函数

诸如基类的重载操作符——这些操作符可能不会像您期望的那样工作,并且应该以您为每个类的每个操作符覆盖的方式实现。

基类的友元函数。

现在关于朋友说明符,这是来自 cpp 参考 here 在这里,您将获得有关如何使用它的示例和详细说明。

您还可以在此处找到有关标准库的大量信息的示例,您还可以了解未来标准中的内容以及您的编译器支持的功能。

【讨论】:

这不是一个完整的答案。外部类的私有成员和受保护成员可从嵌套类访问:eel.is/c++draft/class.access.nest#1 "protected - 函数和变量可以在同一个类和派生类中访问" 您混淆了成员继承和成员可访问性。类的受保护成员不能访问(如base.member)派生类,但继承成员派生类是(如derived.member)——除非它们不是非静态数据成员也不是非静态成员函数(参见[class.access/class.protected-1])。 作为成员函数的重载运算符与任何其他成员函数完全一样被继承。 (一个问题是每个类都至少有一个隐式声明的operator=,它隐藏了operator= 基函数,但它们仍然是继承的成员。) 这就是我的意思,但应该更清楚。至于运营商,我相信有一些特殊情况,至少在我不久前了解的时候是这样。我确实忘记包含嵌套类并且可以添加它。至于受保护的成员,这就是我所得到的,应该在那里更清楚。 嵌套类在基类的范围内。【参考方案2】:

您忽略了一种不那么直观的访问方式,c++ 依赖于编译,其中一个影响是所有实例在内存中都是“相同的”,并且如果该类上的函数可以接受“的引用”,则可以访问彼此的私有变量他自己”,更多细节请查看类变量this

#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

【讨论】:

以上是关于这是对成员访问规则的正确总结吗?的主要内容,如果未能解决你的问题,请参考以下文章

面向对象总结

继承是真的吗?

回顾C++:访问权限控制之 公有保护私有继承用法详解与总结

请教C++高手: 关于类的继承,当子类以public方式继承基类之后,根据继承的规则,基类的所有数据成员和成

总结

急急急!!!!C# 中关于子类访问基类成员的可访问性