14

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

4

3 回答 3

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

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

于 2010-04-01T03:22:09.203 回答
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;
};

我发现这非常令人困惑并且可能非常容易出错(假设我们希望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) {};
};

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

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

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

};
于 2010-04-01T15:37:42.803 回答
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;
}
于 2010-04-01T04:31:21.140 回答