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

时间:2022-09-25 14:47:03

I get a compile error, which I'm slightly confused about. This is on VS2003.

我得到一个编译错误,我有点困惑。这是在VS2003上。

error C2248: 'A::y' : cannot access protected member declared in class 'A'

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

The problem is with x = item.y;

问题是x = item.y;

The access is specified as protected. Why doesn't the constructor of class B have access to A::y?

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

3 个解决方案

#1


5  

It's because of this:

这是因为:

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

If that were legal, you could do this:

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

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

And you'd be calling a protected member of A from B, which isn't allowed.

并且您将从B调用受保护的A成员,这是不允许的。

#2


3  

The other answers explain the reasoning behind preventing your B object from accessing the protected parts of A in your example, even though B 'is-a' A. Of course, the easiest way to fix this problem is to make the parts of A you want access topublic` or have publicly accessible accessor methods.

其他答案解释了防止你的B对象在你的例子中访问A的受保护部分的原因,即使B'是-a'A。当然,解决这个问题的最简单方法是制作A你的部分想要访问topublic`或具有可公开访问的访问方法。

However you might decide that's inappropriate (or you might not have control over the definition of A). Here are some suggestions to let you work around the problem, in increasing order of subverting A's access control. Note that all of these workarounds assume that class A is copy-constructable.

但是,您可能认为这是不合适的(或者您可能无法控制A的定义)。以下是一些建议,让您解决问题,增加颠覆A的访问控制的顺序。请注意,所有这些变通方法都假定类A是可复制构造的。

In the first case, you simply use the copy constructor for A to set up an initial state for that part of the B object, then fix it up afterward:

在第一种情况下,您只需使用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;
};

I find that incredibly confusing and probably very error prone (assuming that we want the A sub-object in the B object to be different than the A object being passed to the constructor - an unusual situation, but it's what was given in the problem). However, the fact that it can be done gives some justification for the more subversive examples that follow...

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

The next example creates a temporary B object that has an exact duplicate of the A object we want access to. We can then use the temporary B object to get to the items that were protected:

下一个示例创建一个临时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) {};
};

I find that solution to be a bit less confusing than the first, but it's still confusing (in my opinion). Having a bastard version of of B object just to get to the parts of the A object we want is odd.

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

We can do something about that by creating a special proxy for A objects that gives the access we want. Note that this is the 'most subversive' workaround because it's something that any class could do to get to protected parts of A, even if they aren't sub-classes of A themselves. In the case of the B class, there's some legitimacy to getting to the protected parts of A objects, since B is-a A, and as we've already seen there are workarounds that let us get access that use only rights that class B already has, so I consider this a cleaner version of those workarounds in class B's case.

我们可以通过为A对象创建一个特殊代理来提供我们想要的访问权限。请注意,这是“最具颠覆性”的解决方法,因为它是任何类都可以做到的,以获取A的受保护部分,即使它们不是A本身的子类。在B类的情况下,获得A对象的受保护部分有一定的合法性,因为B是-A A,而且我们已经看到有一些变通方法让我们获得仅使用B类权限的访问权限已经有了,所以我认为这是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;};
    };

};

#3


1  

IBM's documentation summarizes it best:

IBM的文档最好地总结了它:

A protected nonstatic base class member can be accessed by members and friends of any classes derived from that base class by using one of the following:

通过使用以下之一,可以由从该基类派生的任何类的成员和朋友访问受保护的非静态基类成员:

  • A pointer to a directly or indirectly derived class
  • 指向直接或间接派生类的指针
  • A reference to a directly or indirectly derived class
  • 对直接或间接派生类的引用
  • An object of a directly or indirectly derived class
  • 直接或间接派生类的对象

Thus, using your example above as the basis:

因此,使用上面的示例作为基础:

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

#1


5  

It's because of this:

这是因为:

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

If that were legal, you could do this:

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

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

And you'd be calling a protected member of A from B, which isn't allowed.

并且您将从B调用受保护的A成员,这是不允许的。

#2


3  

The other answers explain the reasoning behind preventing your B object from accessing the protected parts of A in your example, even though B 'is-a' A. Of course, the easiest way to fix this problem is to make the parts of A you want access topublic` or have publicly accessible accessor methods.

其他答案解释了防止你的B对象在你的例子中访问A的受保护部分的原因,即使B'是-a'A。当然,解决这个问题的最简单方法是制作A你的部分想要访问topublic`或具有可公开访问的访问方法。

However you might decide that's inappropriate (or you might not have control over the definition of A). Here are some suggestions to let you work around the problem, in increasing order of subverting A's access control. Note that all of these workarounds assume that class A is copy-constructable.

但是,您可能认为这是不合适的(或者您可能无法控制A的定义)。以下是一些建议,让您解决问题,增加颠覆A的访问控制的顺序。请注意,所有这些变通方法都假定类A是可复制构造的。

In the first case, you simply use the copy constructor for A to set up an initial state for that part of the B object, then fix it up afterward:

在第一种情况下,您只需使用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;
};

I find that incredibly confusing and probably very error prone (assuming that we want the A sub-object in the B object to be different than the A object being passed to the constructor - an unusual situation, but it's what was given in the problem). However, the fact that it can be done gives some justification for the more subversive examples that follow...

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

The next example creates a temporary B object that has an exact duplicate of the A object we want access to. We can then use the temporary B object to get to the items that were protected:

下一个示例创建一个临时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) {};
};

I find that solution to be a bit less confusing than the first, but it's still confusing (in my opinion). Having a bastard version of of B object just to get to the parts of the A object we want is odd.

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

We can do something about that by creating a special proxy for A objects that gives the access we want. Note that this is the 'most subversive' workaround because it's something that any class could do to get to protected parts of A, even if they aren't sub-classes of A themselves. In the case of the B class, there's some legitimacy to getting to the protected parts of A objects, since B is-a A, and as we've already seen there are workarounds that let us get access that use only rights that class B already has, so I consider this a cleaner version of those workarounds in class B's case.

我们可以通过为A对象创建一个特殊代理来提供我们想要的访问权限。请注意,这是“最具颠覆性”的解决方法,因为它是任何类都可以做到的,以获取A的受保护部分,即使它们不是A本身的子类。在B类的情况下,获得A对象的受保护部分有一定的合法性,因为B是-A A,而且我们已经看到有一些变通方法让我们获得仅使用B类权限的访问权限已经有了,所以我认为这是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;};
    };

};

#3


1  

IBM's documentation summarizes it best:

IBM的文档最好地总结了它:

A protected nonstatic base class member can be accessed by members and friends of any classes derived from that base class by using one of the following:

通过使用以下之一,可以由从该基类派生的任何类的成员和朋友访问受保护的非静态基类成员:

  • A pointer to a directly or indirectly derived class
  • 指向直接或间接派生类的指针
  • A reference to a directly or indirectly derived class
  • 对直接或间接派生类的引用
  • An object of a directly or indirectly derived class
  • 直接或间接派生类的对象

Thus, using your example above as the basis:

因此,使用上面的示例作为基础:

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