为什么受保护的超类成员在作为参数传递时无法在子类函数中访问?

时间:2010-04-01 03:18:22

标签: c++ member protected superclass

我收到编译错误,我对此感到有些困惑。这是在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?

3 个答案:

答案 0 :(得分:5)

正因为如此:

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成员,这是不允许的。

答案 1 :(得分:3)

其他答案解释了阻止B对象访问示例中A的受保护部分的原因,即使B'是一个'A 。当然,解决此问题的最简单方法是使A you want access to部分公开或具有可公开访问的访问方法。

但是,您可能认为这是不合适的(或者您可能无法控制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;
};

我发现令人难以置信的混淆并且可能非常容易出错(假设我们希望A对象中的B子对象与传递给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个对象的受保护部分有一定的合法性,因为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;}; }; }; 案例中这些变通办法的更清晰版本。

{{1}}

答案 2 :(得分:1)

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;
}