1

2011 C++ 標準は、セクション 11.4 で次のように述べています。

非静的データメンバーまたは非静的メンバー関数がその命名クラス (11.2) の保護されたメンバーである場合、条項 11 で前述したものを超える追加のアクセスチェックが適用されます。参照は、一部のクラス C のフレンドまたはメンバーで発生します。アクセスがメンバーへのポインターを形成することである場合 (5.3.1)、nested-name-specifier は 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) 標準はメンバーへのポインタと「オブジェクト式」を区別するように注意していますが、標準とコンパイラの両方が同じ制限を与えています。Derived(4)メンバーが中間のベースで宣言された「中間」基本クラス (例) の保護されたメンバーにアクセスすることが合法であるかどうかは不明です。

混乱するのは、祖父母の保護されたメンバーを私の親が所有していると私が話してもよいかどうかということです。ダブルミーニングは意図されていません。

friends(単純さと正気のために無視しています。)

言語の基本的な部分なのでprotected、よく理解したいと思っています。お願いします:

  1. 標準の解釈に基づいて、どのコンパイラがこれを正しく実装していますか?
  2. 全体的な制限の根拠は何ですか? 基本クラスの保護されたメンバーに自由にアクセスできないのはなぜですか? 回避するために設計された具体的なエラーは何ですか? この理論的根拠を探るオンライン ディスカッション (理想的には標準化委員会が開催するもの) を知っていますか?
4

3 に答える 3

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
}

制限されていない場合は、共通の基本型にキャストするだけで、 OtherGrandchildfromの非公開メンバーを変更できます。Grandchildそのようなアクセスは、フレンド宣言を通じてのみ許可されるべきです。

このトピックに関する議論についてはよくわかりませんが、それが私の解釈です。

于 2013-01-25T15:13:38.587 に答える
0

この理由は、派生オブジェクト D は、 D または D から派生した別のクラスであるオブジェクト式を使用して、 base の保護されたメンバーにのみアクセスできるためです。

標準では、D が型 B のオブジェクト式を使用して別のオブジェクトの保護されたメンバーにアクセスすることは許可されていません

上記の引用が言おうとしていることは、正確に(大まかにOK)それです。

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 でもアクセスできません。考慮が必要なのは保護されたメンバーだけです。そして、スタンダードからの上記の引用は、その考慮事項を綴っています。B の保護された非静的メンバーは、型 D または D からさらに派生した型のオブジェクト式を使用してのみ、派生クラス D でアクセスできます。

于 2013-01-25T15:36:22.993 に答える