【问题标题】:Why protected superclass member cannot be accessed in a subclass function when passed as an argument?当作为参数传递时,为什么不能在子类函数中访问受保护的超类成员?
【发布时间】:2011-02-03 05:12:33
【问题描述】:

我收到一个编译错误,对此我有些困惑。这是在 VS2003 上。

错误 C2248:“A::y”:无法访问在“A”类中声明的受保护成员

class A
{
public:
  A() : x(0), y(0) {}
protected:
  int x;
  int y;
};

class B : public A
{
public:
  B() : A(), z(0) {}
  B(const A& item) : A(), z(1) { x = item.y;}
private:
  int z;
};

问题在于 x = item.y;

访问被指定为受保护。为什么B类的构造函数不能访问A::y?

【问题讨论】:

    标签: c++ member protected superclass


    【解决方案1】:

    因为这个:

    class base_class
    {
    protected:
        virtual void foo() { std::cout << "base::foo()" << std::endl; }
    };
    
    class A : public base_class
    {
    protected:
        virtual void foo() { std::cout << "A::foo()" << std::endl; }
    };
    
    class B : public base_class
    {
    protected:
        virtual void foo() { std::cout << "B::foo()" << std::endl; }
    
    public:
        void bar(base_class *b) { b->foo(); }
    };
    

    如果这是合法的,你可以这样做:

    A a;
    B b;
    b.bar(&a);
    

    你会从 B 调用 A 的 protected 成员,这是不允许的。

    【讨论】:

    • 也许你忘了virtual
    • Pavel:哎呀,是的,我添加了 virtual 关键字……但这并没有真正改变我的观点:)
    【解决方案2】:

    IBM 的文档总结得最好:

    受保护的非静态基类 成员可以被成员访问,并且 派生自的任何类的朋友 该基类通过使用其中之一 以下:

    • 指向直接或间接派生类的指针
    • 对直接或间接派生类的引用
    • 直接或间接派生类的对象

    因此,以您上面的示例为基础:

    B::B(const A& item) : A(), z(1) {
      // NOT OK because `item` is not a reference to the derived class B
      //int i = item.y; 
    
      // OK because `item` reinterpreted as a reference to the derived class B
      // Do not do this (bad!) -- for illustrative purposes only
      int i = reinterpret_cast< const B& >(item).y;
    
      // OK because it is equivalent to `this->x = i`,
      //  where `this` is a pointer to the derived class B
      x = i;
    }
    

    【讨论】:

      【解决方案3】:

      其他答案解释了在您的示例中阻止您的 B 对象访问 A 的受保护部分的原因,即使 B 'is-a' A 也是如此。当然,解决这个问题最简单的方法是制作 A you want access topublic` 的部分或具有可公开访问的访问器方法。

      但是,您可能会认为这是不合适的(或者您可能无法控制 A 的定义)。这里有一些建议可以让你解决这个问题,按照破坏A的访问控制的递增顺序。请注意,所有这些变通方法都假定 class A 是可复制构造的。

      在第一种情况下,您只需使用 A 的复制构造函数为 B 对象的该部分设置初始状态,然后再对其进行修复:

      class B1 : public A
      {
      public:
        B1() : A(), z(0) {}
        B1(const A& item) : A(item), z(1) {
          // fix up the A sub-object that was copy constructed 
          //  not quite the way we wanted
          x = y;
          y = 0;
        }
      private:
        int z;
      };
      

      我发现这非常令人困惑并且可能非常容易出错(假设我们希望 B 对象中的 A 子对象与传递给构造函数的 A 对象不同 - 这是一种不寻常的情况,但这是问题中给出的)。然而,它可以做到的事实为接下来的更具颠覆性的例子提供了一些理由......

      下一个示例创建一个临时的B 对象,该对象与我们要访问的A 对象完全相同。然后我们可以使用临时的B 对象来访问受保护的项目:

      class B2 : public A
      {
      public:
        B2() : A(), z(0) {}
        B2(const A& item) : A(), z(1) {
          // create a special-use B2  object that can get to the 
          //  parts of the A object we want access to
          B2 tmp( item, internal_use_only);
      
          x = tmp.y;  // OK since tmp is of type B
        }
      
      private:
        int z;
      
        // create a type that only B2 can use as a 
        //    'marker' to call a special constructor 
        //    whose only purpose in life is to create
        //    a B object with an exact copy of another
        //    A sub-object in it
        enum internal_use {
          internal_use_only
        };
        B2( const A& item, internal_use marker) : A(item), z(0) {};
      };
      

      我发现该解决方案比第一个解决方案更容易混淆,但它仍然令人困惑(在我看来)。仅仅为了获得我们想要的 A 对象的部分而拥有 B 对象的混蛋版本是很奇怪的。

      我们可以通过为A 对象创建一个特殊的代理来解决这个问题,它可以提供我们想要的访问权限。请注意,这是“最具颠覆性”的解决方法,因为任何类都可以这样做来获取A 的受保护部分,即使它们本身不是A 的子类。在B 类的情况下,访问A 对象的受保护部分是合法的,因为B is-a A,正如我们已经看到的,有一些变通方法可以让我们得到仅使用class B 已经拥有的权限的访问权限,因此我认为这是class B 案例中这些变通办法的更简洁版本。

      class B3 : public A
      {
      public:
        B3() : A(), z(0) {}
        B3(const A& item) : A(), z(1) { 
          // a special proxy for A objects that lets us
          //  get to the parts of A we're interested in
          A_proxy tmp( item);
          x = tmp.get_y();
        }
      
      private:
        int z;
      
          class A_proxy : public A
          {
          public:
              A_proxy( const A& other) : A(other) {};
              int get_x() {return x;};
              int get_y() {return y;};
          };
      
      };
      

      【讨论】:

      • 感谢您的回答。确实,你的假设是正确的。我希望 B 对象中的 A 子对象与传递给构造函数的 A 对象不同。
      • 在现实生活中,我需要复制的所有信息都可以通过 A 的公共接口获得,所以我只是使用它而不是访问受保护的成员。实际上,受保护的 A 成员最初是私有的,我将其设置为受保护以便能够从 B 访问它。
      • @Michael Burr - 有没有办法直接更改 A 的受保护成员(不是您显示的 A 的副本)?
      猜你喜欢
      • 1970-01-01
      • 2013-03-05
      • 2012-12-01
      • 2022-12-05
      • 2017-06-04
      • 2011-10-06
      • 1970-01-01
      • 2021-01-22
      • 1970-01-01
      相关资源
      最近更新 更多