C++ 访问另一个子类中基类的受保护成员

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11631777/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 15:22:46  来源:igfitidea点击:

accessing a protected member of a base class in another subclass

c++inheritanceencapsulationprotected

提问by Kaiserludi

Why does this compile:

为什么这样编译:

class FooBase
{
protected:
    void fooBase(void);
};

class Foo : public FooBase
{
public:
    void foo(Foo& fooBar)
    {
        fooBar.fooBase();
    }
};

but this does not?

但这不?

class FooBase
{
protected:
    void fooBase(void);
};

class Foo : public FooBase
{
public:
    void foo(FooBase& fooBar)
    {
        fooBar.fooBase();
    }
};

On the one hand C++ grants access to private/protected members for all instances of that class, but on the other hand it does not grant access to protected members of a base class for all instances of a subclass. This looks rather inconsistent to me.

一方面,C++ 授予对该类的所有实例的私有/受保护成员的访问权限,但另一方面,它不授予对子类的所有实例的基类的受保护成员的访问权限。这对我来说看起来很不一致。

I have tested compiling with VC++ and with ideone.com and both compile the first but not the second code snippet.

我已经测试了使用 VC++ 和 ideone.com 进行编译,并且都编译了第一个而不是第二个代码片段。

采纳答案by Rob Kennedy

When fooreceives a FooBasereference, the compiler doesn't know whether the argument is a descendant of Foo, so it has to assume it's not. Foohas access to inherited protected members of other Fooobjects, not all other sibling classes.

foo接收到一个FooBase引用时,编译器不知道该参数是否是 的后代Foo,所以它必须假设它不是。Foo可以访问其他Foo对象的继承保护成员,而不是所有其他同级类。

Consider this code:

考虑这个代码:

class FooSibling: public FooBase { };

FooSibling sib;
Foo f;
f.foo(sib); // calls sib.fooBase()!?

If Foo::foocan call protected members of arbitrary FooBasedescendants, then it can call the protected method of FooSibling, which has no direct relationship to Foo. That's not how protected access is supposed to work.

如果Foo::foo可以调用任意FooBase后代的受保护成员,那么它可以调用 的受保护方法FooSibling,该方法与没有直接关系Foo。这不是受保护的访问应该如何工作。

If Fooneeds access to protected members of all FooBaseobjects, not just those that are also known to be Foodescendants, then Fooneeds to be a friend of FooBase:

如果Foo需要访问所有FooBase对象的受保护成员,而不仅仅是那些也已知是Foo后代的成员,则Foo需要成为以下对象的朋友FooBase

class FooBase
{
protected:
  void fooBase(void);
  friend class Foo;
};

回答by h0b0

The C++ FAQsummarizes this issue nicely:

C ++ FAQ很好地总结了这个问题:

[You] are allowed to pick your own pockets, but you are not allowed to pick your father's pockets nor your brother's pockets.

[你]可以扒自己的口袋,但不能扒你父亲的口袋,也不能扒你兄弟的口袋。

回答by David Rodríguez - dribeas

The key point is that protectedgrants you access to your own copy of the member, not to those members in anyother object. This is a common misconception, as more often than not we generalize and state protectedgrants access to the member to the derived type (without explicitly stating that only to their own bases...)

关键点是protected授予您访问您自己的成员副本的权限,而不是任何其他对象中的那些成员。这是一个常见的误解,因为我们通常会概括和状态protected授予对派生类型成员的访问权限(没有明确说明仅对他们自己的基础......)

Now, that is for a reason, and in general you should not access the member in a different branch of the hierarchy, as you might break the invariants on which other objects depend. Consider a type that performs an expensive calculation on some large data member (protected) and two derived types that caches the result following different strategies:

现在,这是有原因的,通常您不应访问层次结构不同分支中的成员,因为您可能会破坏其他对象所依赖的不变量。考虑一种对某些大数据成员(受保护)执行昂贵计算的类型和两种派生类型,它们按照不同的策略缓存结果:

class base {
protected:
   LargeData data;
// ...
public:
   virtual int result() const;      // expensive calculation
   virtual void modify();           // modifies data
};
class cache_on_read : base {
private:
   mutable bool cached;
   mutable int cache_value;
// ...
   virtual int result() const {
       if (cached) return cache_value;
       cache_value = base::result();
       cached = true;
   }
   virtual void modify() {
       cached = false;
       base::modify();
   }
};
class cache_on_write : base {
   int result_value;
   virtual int result() const {
      return result_value;
   }
   virtual void modify() {
      base::modify();
      result_value = base::result(); 
   }
};

The cache_on_readtype captures modifications to the data and marks the result as invalid, so that the next readof the value recalculates. This is a good approach if the number of writes is relatively high, as we only perform the calculation on demand (i.e. multiple modifies will not trigger recalculations). The cache_on_writeprecalculates the result upfront, which might be a good strategy if the number of writes is small, and you want deterministic costs for the read (think low latency on reads).

cache_on_read类型捕获对数据的修改并将结果标记为无效,以便重新计算下一次读取值。如果写入次数比较多,这是一个很好的方法,因为我们只执行按需计算(即多次修改不会触发重新计算)。在cache_on_write预先计算的结果前期,这可能是一个很好的策略,如果写入次数少,你想为读确定性成本(想想上读取低延迟)。

Now, back to the original problem. Both cache strategies maintain a stricter set of invariants than the base. In the first case, the extra invariant is that cachedis trueonly if datahas not been modified after the last read. In the second case, the extra invariant is that result_valueis the value of the operation at all times.

现在,回到最初的问题。两种缓存策略都维护了一组比基数更严格的不变量。在第一种情况下,额外不变的是,cachedtrue只有在data还没有被最后一次读取后修改。在第二种情况下,额外的不变量是result_value始终操作的值。

If a third derived type took a reference to a baseand accessed datato write (if protectedallowed it to), then it would break with the invariants of the derived types.

如果第三个派生类型base获取data对a 的引用并访问写入(如果protected允许),那么它会破坏派生类型的不变量。

That being said, the specification of the language is broken(personal opinion) as it leaves a backdoor to achieve that particular result. In particular, if you create a pointer to member of a member from a base in a derived type, access is checked in derived, but the returned pointer is a pointer to member of base, which can be applied to anybaseobject:

话虽如此,该语言的规范已被破坏(个人意见),因为它为实现该特定结果留下了后门。特别是,如果您从派生类型中的基类创建指向成员成员的指针,则会检入访问derived,但返回的指针是指向 成员的指针base,该指针可以应用于任何base对象:

class base {
protected:
   int x;
};
struct derived : base {
   static void modify( base& b ) {
      // b.x = 5;                        // error!
      b.*(&derived::x) = 5;              // allowed ?!?!?!
   }
}

回答by Zeta

In both examples Fooinherits a protected method fooBase. However, in your first example you try to access the given protected method from the same class (Foo::foocalls Foo::fooBase), while in the second example you try to access a protected method from another class which isn't declared as friend class (Foo::footries to call FooBase::fooBase, which fails, the later is protected).

在这两个例子中都Foo继承了一个受保护的方法fooBase。但是,在第一个示例中,您尝试从同一类(Foo::foo调用Foo::fooBase)访问给定的受保护方法,而在第二个示例中,您尝试从未声明为友元类的另一个类访问受保护的方法(Foo::foo尝试调用FooBase::fooBase,失败,后者受到保护)。

回答by Moataz Elmasry

In the first example you pass an object of type Foo, which obviously inherits the method fooBase() and so is able to call it. In the second example you are trying to call a protected function, simply so, regardless in which context you can't call a protected function from a class instance where its declared so. In the first example you inherit the protected method fooBase, and so you have the right to call it WITHIN Foo context

在第一个示例中,您传递了一个 Foo 类型的对象,该对象显然继承了 fooBase() 方法,因此可以调用它。在第二个示例中,您尝试调用受保护的函数,简单来说,无论在哪个上下文中都不能从声明为如此的类实例调用受保护的函数。在第一个示例中,您继承了受保护的方法 fooBase,因此您有权在 WITHIN Foo 上下文中调用它

回答by Sentinel

I tend to see things in terms of concepts and messages. If your FooBase method was actually called "SendMessage" and Foo was "EnglishSpeakingPerson" and FooBase was SpeakingPerson, your protecteddeclaration is intended to restrict SendMessage to between EnglishSpeakingPersons (and subclasses eg: AmericanEnglishSpeakingPerson, AustralianEnglishSpeakingPerson) . Another type FrenchSpeakingPerson derived from SpeakingPerson would not be able to receive a SendMessage, unless you declared the FrenchSpeakingPerson as a friend, where 'friend' meant that the FrenchSpeakingPerson has a special ability to receive SendMessage from EnglishSpeakingPerson (ie can understand English).

我倾向于从概念和信息的角度看待事物。如果您的 FooBase 方法实际上被称为“SendMessage”,而 Foo 是“EnglishSpeakingPerson”而 FooBase 是SpeakingPerson,则您的受保护声明旨在将 SendMessage 限制在 EnglishSpeakingPersons (和子类,例如: AmericanEnglishSpeakingPerson、AustralianEnglishSpeakingPerson)之间。从SpeakingPerson 派生的另一种类型FrenchSpeakingPerson 将无法接收SendMessage,除非您将FrenchSpeakingPerson 声明为朋友,其中“朋友”意味着FrenchSpeakingPerson 具有从EnglishSpeakingPerson 接收SendMessage 的特殊能力(即可以理解英语)。

回答by yairchu

In addition to hobo's answeryou may seek a workaround.

除了流浪汉的回答之外,您还可以寻求解决方法。

If you want the subclasses to want to call the fooBasemethod you can make it static. static protected methods are accessible by subclasses with all arguments.

如果您希望子类想要调用该fooBase方法,您可以使用它static。带有所有参数的子类可以访问静态保护方法。