11

基本クラス A と派生クラス B があります

クラス B は A からパブリックとして派生します

Aがクラスの場合、メンバー変数のアドレスにアクセスしたいaがメンバー変数である

保護されたパブリック アクセス指定子を使用している場合、異なる動作を観察しています。

この場合、クラス A のメンバー a が保護されている場合、次のようになります。

cout<<&A::a << endl;私にコンパイラエラーを投げます..

しかしcout<<&(A::a)<<endl;、有効であり、適切な結果が得られます。

この場合、クラス A のメンバー a がpublicの場合、次のようになります。

なぜこの動作ですか?

完全なコードは次のとおりです。

#include <iostream>
using namespace std;

class A
{
    protected:
    int a;
    void fun() 
    {
    cout<<"A's fun"<<endl;
    }

public:
    A(int Aarg):a(Aarg)
    {
        cout << "A's construction done" <<endl;
    }
};


class B: public A
{
protected:
int b;
public:
void fun()
{
cout << "B's fun"<<endl;
}

B(int As, int Bs):A(As), b(Bs)
{
std::cout<<"B's Construction Done" <<std::endl;
}

void show()
{
A::fun();
//cout << a << endl;
cout<<&A::a << endl; //compilation error
}
};

int main()
{
    B(10,20).show();
    return 0;
}

今、私が観察できる未定義の動作があります:

クラス A のメンバー変数 a を public にすると、コンパイル エラーは発生しませんが、出力が 1 になる理由はわかりません。

class A{
public:
int a
....
....

出力:

Aの施工完了

Bさんの施工完了

Aの楽しみ

0027F91C

1 (なぜ 1) で、保護されたメンバーにアクセスしようとしたときに取得できたエラーはありませんか?

4

3 に答える 3

4

構文に小さな癖があります (C++ にそれらがほとんどないというわけではありません...)。メンバー変数にアクセスするデフォルトの方法は、名前を直接使用するか、this->. つまり、show 関数のより簡単なスペルは次のようになります。

void B::show() {
   std::cout << a << std::endl;     // alternatively this->a
   std::cout << &a << std::endl;    //               &(this->a)
}

シンプルで一貫した構文を持っています。現在、この言語では、メンバーにアクセスするときに、ベースのメンバーにアクセスするために追加の修飾子をドロップできます。

std::cout << A::a << std::endl;     // Extra qualification

これは、動的ディスパッチを無効にする仮想関数の場合と実際には同等でthis->A::aあり、追加の修飾の主な用途は、あいまいさを解消することです (2 つのベースに member がある場合はa、1 つを選択します)。A

のように、現在のオブジェクトのサブオブジェクト内&this->A::aのメンバーのアドレスを取得するポインターで同じことができるようになりました。この場合の問題は、構文がメンバーへのポインターを取得するために予約されているため、修飾子を削除できないことです。で表されるオブジェクトのアドレスを取得するのではなく、前に見たようにベースのメンバーです。aAthis->&A::a&(A::a)A::a

于 2013-01-18T15:33:05.810 に答える
3

簡単な答え:未定義の動作は含まれていません。表示される動作は次のとおりです。

  • この式&A::aは、クラスAのメンバーaを指すメンバーへのポインターを取得しようとします。aがAで保護されている場合、この式はクラスA(またはAの友達)内のアクセスチェックにのみ合格します。Aから派生したクラスBでは、式を介してのみメンバーへの同じポインターを取得できます&B::a(この式のタイプは引き続きであることに注意してくださいint A::*)。それで:
    • A::aAで保護されている場合、式&A::aは派生クラスBのメンバー関数で許可されていません。これはコンパイラエラーです。
    • Aでpublicの場合A::a、この式は有効であり、memeberへのポインターを生成します。
  • メンバーへのポインタをにストリーミングするostream、たとえばを使用cout << &A::aすると、が出力されます1。これは、を呼び出した結果ostream::operator << (bool)です。boolalphaマニピュレータを使用して、これが実際に選択されたオーバーロードであることを確認できcout << boolalpha << &A::aますtrue
  • 変更された式&(A :: a)または単に&aを使用する場合、メンバーへのポインターは形成されません。ここでは、現在のオブジェクトのメンバーaのアドレス(つまり、と同じ&(this->a))が取得されます。これは、intへの通常のポインターです。の基本クラスサブオブジェクトの保護されたメンバーへのこのアクセス*thisは有効であるため、aがAで保護されている場合でもバリアントを使用できます。

より長い説明:

標準によると(5.3.1 / 3):

単項&演算子の結果は、そのオペランドへのポインターです。オペランドは左辺値または修飾IDでなければなりません。オペランドが、タイプTのクラスCの非静的メンバーmに名前を付ける修飾IDである場合、結果はタイプ「タイプTのクラスCのメンバーへのポインター」を持ち、C::mを指定するprvalueになります。[...]

したがって、式&A::aはクラスAのメンバーaへのメンバーへのポインターを取得しようとします。

次の段落(5.3.1 / 4)では、&X ::m構文のみがメンバーへのポインターを生成することを詳しく説明してい&(X::m)ます。&mX::m

メンバーへのポインターは、明示的な&が使用され、そのオペランドが括弧で囲まれていない修飾IDである場合にのみ形成されます。

ただし、このような式は、アクセスが許可されている場合にのみ有効です。保護されたメンバー(11.4 / 1)の場合:

非静的データメンバーまたは非静的メンバー関数がそのネーミングクラスの保護されたメンバーである場合、第11項で説明したもの以外の追加のアクセスチェックが適用されます(11.2)前述のように、保護されたメンバーへのアクセスは、参照は、あるクラスCの友人またはメンバーで発生します。アクセスがメンバー(5.3.1)へのポインターを形成する場合、ネストされた名前指定子は、CまたはCから派生したクラスを示します。[...]

あなたの場合、保護されたメンバーaへのアクセスが許可されます。これは、aへの参照がAから派生したクラスBのメンバーで発生するためです。式がメンバーへのポインターを形成しようとすると、ネストされた名前指定子(一部最後の"::a")の前はBを表す必要があります。したがって、許可される最も単純な形式は&B::aです。このフォーム&A::aは、クラスA自体のメンバーまたは友人内でのみ許可されます。

メンバーへのポインター用のフォーマットされた出力演算子(istreamメンバーとしてもfree演算子関数としても)がないため、コンパイラーは、標準の変換(シーケンス)を使用して呼び出すことができるオーバーロードを調べます。ポインタからメンバー、他の何かへの唯一の標準的な変換は、4.12/1で説明されています。

メンバー型への[...]ポインタのprvalueは、bool型のprvalueに変換できます。[...]nullメンバーポインタ値はfalseに変換されます。その他の値はすべてtrueに変換されます。[...]

この変換は、を呼び出すための追加の変換なしで使用できますbasic_ostream<charT,traits>& basic_ostream<charT,traits>::operator<<(bool n)。他のオーバーロードはより長い変換シーケンスを必要とするため、オーバーロードが最適です。

一部のメンバーのアドレスを取得するため、これ&A::aはnullメンバーポインター値ではありません。したがって、に変換されtrue、「1」(noboolalpha)または「true」(boolalpha)として出力されます。

最後に、&(A::a)aがAで保護されている場合でも、式はBのメンバーで有効です。上記の規則によってこの式はメンバーへのポインターを形成しないため、上記で引用した特別なアクセス規則は適用されません。このような場合、11.4/1は続きます。

他のすべてのアクセスには、(おそらく暗黙の)オブジェクト式(5.2.5)が含まれます。この場合、オブジェクト式のクラスはCまたはCから派生したクラスでなければなりません。

ここで、オブジェクトの印象は暗黙的です。(*this)つまりA::a、と同じ意味(*this).A::aです。のタイプは(*this)明らかにアクセスが発生するクラス(B)と同じであるため、アクセスが許可されます。[注:int x = A(42).aB内では許可されません。]

つまり、 &(A::a)withinB::show()はと同じ意味で&(this->a)あり、それはintへの単純なポインタです。

于 2013-01-27T00:32:12.570 に答える
-5

構文の問題です。& は、すぐ右側の要素のアドレスを要求することを意味します。

あなたが書く場合:

&A::a

あたかもあなたが書いたかのように

(&A)::a

これは、'&A' から 'a' へのアクセスを要求していることを意味しますが、これは正しくありません。

& は変数だけに適用されるのではなく、関数にも使用できます。次を参照してください: http://www.cprogramming.com/tutorial/function-pointers.html

于 2013-01-18T14:05:28.253 に答える