深入了解C ++受保护

时间:2013-01-25 14:53:13

标签: c++ protected

第11.4节中的2011 C++ Standard states

  

除了前面第11章中描述的那些之外的附加访问检查   在非静态数据成员或非静态成员函数时应用   是其命名类的受保护成员(   11.2)115如前所述,授予对受保护成员的访问权限,因为引用发生在朋友或某些成员中   C级。如果访问是形成指向成员的指针(   5.3.1),嵌套名称说明符应表示C或从C派生的类。所有其他访问涉及(可能是隐式的)对象   表达(   5.2.5)。在这种情况下,对象表达式的类应为C或从C派生的类。

(在旧标准中,类似的措辞在第11.5节中。)

这条规则限制了经常重复的观点,即“B的受保护成员可以被任何B或派生类B访问”。然而,解释规则很困难,因为不同的当前编译器以不同的方式强制执行规则。

例如,请参阅此测试代码。我使用Apple LLVM Compiler 4.1,GCC 4.7.2和Visual Studio 2010编译了这段代码。它们报告的错误有相似之处和不同之处。

class Base
{
protected:
    int A;
};

class Derived : public Base
{
protected:
    int B;
};

class Grandchild : public Derived
{
    void access_protected(Base* b, Derived* d,
                          Grandchild* g, class GreatGrandchild* gg );
};

class GreatGrandchild : public Grandchild {};

void Grandchild::access_protected(Base* b, Derived* d,
                                  Grandchild* g, GreatGrandchild* gg )
{
    int* p;

    Base lb;
    Derived ld;
    Grandchild lg;
    GreatGrandchild lgg;

    A = 1;              // Legal...
    B = 2;

    Base::A = 1;
    Derived::B = 2;

    b->A = 1;           // Illegal ALL
    p = &(b->A);        // Illegal ALL
    lb.A = 1;           // Illegal ALL
    p = &(lb.A);        // Illegal ALL

    d->A = 1;           // Illegal GCC, VS
    p = &(d->A);        // Illegal GCC, VS
    ld.A = 1;           // Illegal GCC, VS
    p = &(ld.A);        // Illegal GCC, VS
    d->B = 2;           // Illegal ALL
    p = &(d->B);        // Illegal ALL
    ld.B = 2;           // Illegal ALL
    p = &(ld.B);        // Illegal ALL

    g->A = 1;           // Legal...
    g->B = 2;
    lg.A = 1;
    lg.B = 2;

    gg->A = 1;
    gg->B = 2;
    lgg.A = 1;
    lgg.B = 2;
}

从这些结果中我得知:(1)访问自己的类和派生类的受保护成员总是可以的; (2)访问声明它们的基类的受保护成员始终是非法的,除了该类; (3)虽然标准注意区分成员指针和“对象表达”,但标准和编译器都给予它们相同的限制; (4)不清楚访问中间基础中声明成员的“中间”基类(示例中为Derived)的受保护成员是否合法。

那么,混淆的是,我是否可以谈论我父母所拥有的祖父母受保护的成员。没有双关语。

(为了简单和理智,我忽略了friends。)

由于protected是语言的基本组成部分,我很有动力去理解它。请:

  1. 根据您对标准的解释,哪个编译器正确实现了这个?
  2. 整体限制的理由是什么?为什么不能我可以自由地访问基类的受保护成员?它旨在避免哪些具体错误?您是否知道在线讨论 - 理想情况下由标准委员会持有 - 探讨了这一理由?

3 个答案:

答案 0 :(得分:0)

我想说GCC和Visual Studio是正确的。

鉴于以下情况:

class Base
{
protected:
  int A;
};

class Derived : public Base
{
protected:
  int B;
};

class OtherGrandchild : Derived
{
};

class Grandchild : Derived
{
  void access_protected(OtherGrandchild* otherGrandchild);
};

void Grandchild::access_protected(OtherGrandchild* otherGrandchild)
{
  otherGrandchild->A = 1; // Should be illegal
  otherGrandchild->B = 1; // Should be illegal

  Derived* derived = static_cast<Derived*>(otherGrandchild);

  derived->A = 1; // Should still be illegal
  derived->B = 1; // Should still be illegal
}

如果您没有受到限制,您可以通过转换为公共基本类型来改变OtherGrandchild的{​​{1}}私密成员。只有朋友声明才能允许这种访问。

我不确定有关该主题的任何讨论,但这将是我对它的解释。

答案 1 :(得分:0)

这样做的原因是派生对象D只能使用D或从D派生的另一个类的对象表达式访问基类的受保护成员。

标准不允许D使用类型B

的对象表达式访问另一个对象的受保护成员

这就是上面引用的内容完全(大致可以)。

class base
{
protected:
   int x;
};

class derived : public base
{
public:
   void f(base *p)   
   {
      x = 2;     // good
      p->x = 3;  // not good. base is not 'derived' nor derived from 'derived'
   }
};

int main() { }

考虑一下。基础B的公共成员可以在任何派生类D中访问.B的私有成员在任何派生类D中都不可访问。只有受保护的成员才需要考虑。以上引用标准说明了这一点。只有使用D类型的对象表达式或从D进一步派生的类型,才能在派生类D中访问B的受保护非静态成员

答案 2 :(得分:0)

我不能引用一个参考或权威来源,但它看起来像g ++和VS在这里都是正确的(这是他们第一次同意吗?)因为它只是没有逻辑意义上的空中间注入类可以改变父母受保护数据的访问控制。

至于理由,几乎可以肯定,因为你protected的限制越少,它就越public(它已经与公众有很多相似之处,因为你必须得到一个获得对父母内部的无限制访问的类。如果你开始允许甚至不是同一个实例的子类来操纵你的状态,那么违反类不变量的可能性就会大大增加。