访问基类的保护构件在另一亚类中(accessing a protected member of a

2019-06-17 12:46发布

为什么这编译:

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

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

但是这不?

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

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

一方面,C ++允许访问该类的所有实例私有/ protected成员,但在另一方面,它不授予访问某个子类的所有实例的基类的保护成员。 这看起来相当不一致的给我。

我已经测试用VC和与ideone.com编译和编译两者在第一而不是第二代码段。

Answer 1:

foo收到FooBase参考,编译器不知道这个说法是否后裔Foo ,所以它必须假设它不是。 Foo先后获得其他的继承保护成员Foo对象 ,而不是其他的兄弟姐妹类。

考虑以下代码:

class FooSibling: public FooBase { };

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

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

如果Foo需要所有受保护的成员访问FooBase对象,不只是那些也被称为是Foo后代,然后Foo需要成为朋友FooBase

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


Answer 2:

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

[你]被允许选择自己的口袋里,但你不能来接你父亲的口袋里,也没有你哥哥的腰包。



Answer 3:

关键的一点是, protected您有权使用自己的成员的副本,而不是那些成员的任何其他对象。 这是一个普遍的误解,随着越来越多的往往不是我们概括和国家protected访问权限授予成员在派生类(但没有明确指出,只是为了自己的基地......)

现在,这是有原因的,一般你不应该访问该成员在层次结构的不同分支,因为你可能会打破这样,其他目标所依赖的不变量。 考虑,其对一些大的数据成员(受保护的)和两个派生类型高速缓存结果以下不同的策略昂贵的计算类型:

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

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

现在,回到原来的问题。 两种缓存策略保持严格不变集比基础的。 在第一种情况下,额外不变的是, cachedtrue只有当data还未最后一次读取后修改。 在第二种情况下,额外不变的是, result_value是运行在任何时候都值。

如果第三派生类型走上一参考base和访问data写入(如果protected允许它),那么它会与派生类型的不变量断裂。

话虽这么说,因为它留下了后门,以实现特定的结果,语言规范被打破 (个人意见)。 特别地,如果创建一个指针从在派生类型的底座的构件的构件,则访问被在检查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 ?!?!?!
   }
}


Answer 4:

在这两个例子中Foo继承的保护方法fooBase 。 然而,在你的第一个例子中,你尝试从同一类访问给定的保护方法( Foo::foo调用Foo::fooBase ),而在第二个例子中,你尝试从未声明的另一个类访问受保护的方法作为朋友类( Foo::foo尝试拨打FooBase::fooBase ,它失败了,后来被保护)。



Answer 5:

在第一个例子传递Foo类型,这显然继承方法fooBase()的目的,并且因此能够调用它。 在第二个例子中,你正试图调用一个受保护的功能,只需所以,无论在哪个方面,你不能调用从那里其宣称的这么一个类的实例保护功能。 在第一个例子,你继承的保护方法fooBase,所以你必须把它WITHIN美孚方面的权利



Answer 6:

我倾向于看到的概念和信息方面的事情。 如果您FooBase方法实际上是所谓的“SendMessage函数”和Foo是“EnglishSpeakingPerson”和FooBase是SpeakingPerson, 受保护的声明意在限制SendMessage函数EnglishSpeakingPersons之间(和子类如:AmericanEnglishSpeakingPerson,AustralianEnglishSpeakingPerson)。 从SpeakingPerson衍生另一种类型FrenchSpeakingPerson将无法接收SendMessage函数,除非你宣布FrenchSpeakingPerson作为一个朋友,在“朋友”指的是FrenchSpeakingPerson必须从EnglishSpeakingPerson收到的SendMessage(即能听懂英语)的特殊能力。



Answer 7:

除了流浪汉的答案 ,你可能会寻求一个解决方法。

如果你想在子类要调用的fooBase方法你可以把它static 。 静态保护的方法是通过与所有参数的子类访问。



文章来源: accessing a protected member of a base class in another subclass