【问题标题】:Visibility of Class members?班级成员的可见性?
【发布时间】:2012-01-01 23:44:40
【问题描述】:

我想我知道可访问性,但我不确定我是否非常清楚地了解可见性

例如:

class X
{
   int x;
};

这里,“x”仅在课堂上可见,但在课堂外可访问。如果我是正确的,有人可以解释该答案中关于可见性如何不受控制等的文字吗?

(C++03/11.0) 需要注意的是访问成员和基类 是受控制的,而不是他们的可见性。成员名字还在 对基类的可见和隐式转换仍然存在 考虑,当这些成员和基类不可访问时。这 对给定结构的解释是建立的,而不考虑 访问控制。如果建立的解释使用 不可访问的成员名称或基类,构造是 格式不正确。

【问题讨论】:

  • 您应该在该答案的 cmets 中要求澄清。
  • @TomalakGeret'kal:我会从我的问题中删除对该答案的引用。
  • 嗯..这与我建议的正确做法相反。

标签: c++ scope visibility


【解决方案1】:

也许这个例子有帮助:

class Bob
{
private:
    int foo(int, int);
};

class David : Bob
{
    void goo() {
        int a = foo(1, 2);  // #1
    }
};

class Dani : Bob
{
     void foo();
     void goo() {
         int a = foo(1, 2); // #2
     }   
};

在第 1 行,名称foo可见的,但它命名的函数是不可访问的(由于是Bob 私有的) .这是一个编译错误,但编译器知道有一个潜在的函数 Bob::foo 可以匹配,但无法访问。

在第 2 行,名称foo 仅指代Dani::foo,而Bob::foo 不可见(因为它隐藏),等等调用foo(1, 2) 根本没有匹配的函数。这也是编译错误,但是这次的错误是调用时根本没有匹配的函数。

【讨论】:

  • 我能知道它会引起什么混乱吗?
  • @user1086635 不确定您的意思:行号与描述不符。第 1 行是“Class Bob”,但他的意思似乎是“int foo(int, int);”第 2 行是“{”。措辞需要修改。
  • @Serodis:引用是对标有 cmets // #1 等的行的引用。这种注释样式在 SO 上很常见,因为源块没有任何其他形式的容易可见的编号。
  • @KerrekSB:你能告诉我从有问题的引用文本中and implicit conversions to base classes are still considered 是什么意思吗?谢谢。
  • @user1086635:如果你有一个函数void f(Bob&),并且你会尝试用David对象传递它,那么隐式转换David->Bob就会被选中。由于BobDavid 的私有基类,即使基类可见,这种(隐式)转换也无法访问。
【解决方案2】:

C++ 有一些关于私有类成员名称可见性和可访问性的深奥特性。根据定义,私有类成员名称只能由类成员和朋友访问。然而,可见性规则可能会使许多人感到困惑。它们可以总结如下。

  1. 私人成员的名字只有其他成员和朋友才能访问。
  2. 看到类定义的所有代码都可以看到私有成员。这意味着它的参数类型必须声明,即使在这个翻译单元中永远不需要它们......
  3. 重载解决方案在可访问性检查之前发生。

在今天的 C++(“C++03”和更早的变体)中,可访问性和可见性的概念是 独立的。类和命名空间的成员只要在“in 范围”,并且没有任何机制可以从声明的角度降低这种可见性。 可访问性只是类成员的一个参数,与 能见度。后一种观察结果常常令 C++ 程序员新手感到惊讶。看到这个PDF

考虑以下示例。

#include < complex>

class Calc 
{ 
    public: 
        double Twice( double d ); 
    private: 
        int Twice( int i ); 
        std::complex Twice( std::complex c ); 
};

int main() 
{ 
    Calc c; 
    return c.Twice( 21 ); // error, Twice is inaccessible 
}    

当编译器必须解析对函数的调用时,它会按顺序执行三项主要操作:

  • 在执行任何其他操作之前,编译器会搜索一个范围 至少有一个名为 Twice 的实体并列出了一个候选者列表。 在这种情况下,名称查找首先在 Calc 的范围内查找,看看是否 至少有一个名为 Twice 的函数;如果没有,基地 将依次考虑类和封闭的命名空间,一个在一个 时间,直到找到具有至少一个候选者的范围。在这个 但是,在这种情况下,编译器查看的第一个范围已经有了 一个名为 Twice 的实体——事实上,它有三个,所以 trio 成为候选集。 (有关名称的更多信息 在 C++ 中查找,并讨论它如何影响您的方式 应该打包你的类和它们的接口

  • 接下来,编译器执行重载解析来选择唯一的 候选人名单中的最佳匹配。在这种情况下,论据 是 21,它是一个 int,并且可用的重载需要一个 double,一个 int 和一个复数。显然 int 参数是最好的匹配 int 参数(它是完全匹配的,没有转换 必需),因此选择了 Twice(int)。

  • 最后,编译器执行可访问性检查以确定 是否可以调用选中的函数。

请注意,accessibility(由 C++ 中的修饰符定义)和 visibility 是独立的。可见性基于 C++ 的范围规则。一个类成员可以同时可见不可访问

以静态成员为例,在您的应用程序运行期间全局可见,但可访问仅对于应用于它们的修饰符。

【讨论】:

    【解决方案3】:

    请注意:当您声明 class 时,默认范围是私有的(与默认情况下成员为公共的 struct 相对。)

    变量成员“x”只能由您的班级及其朋友访问。没有其他人可以直接访问“x”(如果您有一个函数返回对它的引用,它可以间接访问,这是一个非常糟糕的主意。)

    您引用的文本谈到了编译器的可见性,所以X::x 无论如何都存在。它不会仅仅因为它是私有的而消失。可见性用于查找您正在引用的成员,并返回第一个匹配的成员。此时编译器会检查可访问性,如果可访问,则一切正常。如果不是,则它是格式错误的

    请注意,我提到了朋友。该关键字使所有变量成员都可以访问。当编译器与朋友打交道时,它会完全忽略所有受保护的和私有的关键字。

    在大多数情况下,这是一个非常简单的过程。它按顺序进行。期间。

    当您开始使用虚函数时变得更加复杂:这些可以设为公共的、受保护的和私有的,并且可以根据类声明而改变...(A 派生自 B 并将受保护的虚函数设为公共; 这通常不是一个好主意,但 C++ 不会阻止你这样做。)当然这仅适用于函数,不适用于变量成员,所以这是一个不同的主题。

    【讨论】:

      【解决方案4】:

      可访问性和可见性是独立的,尤其是在这种情况下:

      class A
      {
      public:
        void Foo(int i){
        }
      };
      
      class B : public A
      {
      private:
        void Foo(){
        }
      };
      
      int main(){
          B b{};
      
          b.Foo(12);
      }
      

      其他语言的程序员会期望A::Foo(int) 是可调用的,因为它是公开的。这里的重点是,私有 B::Foo 隐藏了继承的 proc。

      这可以通过使用声明 using A::Foo 来解决。但是在这种情况下变得非常困难:

      class A
      {
      public:
        void Foo(int i){
        }
      };
      
      class B : public A
      {
      public:
        using A::Foo;
      private:
        void Foo(){
        }
      };
      
      
      class C : public B
      {
      public:
        using B::Foo;
      private:
        void Foo(char c){
        }
      }
      
      int main(){
          B b{};
      
          b.Foo(12);
      }
      

      使用要求没有私有函数。 AFAIK 解决它的最佳方法是为私有或受保护函数(如 do_XXX() 或 do_XXX_internal)使用一些前缀/后缀。

      在 STL 中,私有成员通常以单个下划线为前缀(这些是保留标识符)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-09
        • 1970-01-01
        • 2012-05-26
        • 1970-01-01
        • 2020-12-26
        • 1970-01-01
        相关资源
        最近更新 更多