C++のネストクラスと周辺クラスおよび友元

3963 ワード

class Outer
{
public:
    Outer(){m_outerInt=0;}
private:
    int m_outerInt;
public:
    //       
    class Inner
    {
    public:
        Inner(){m_innerInt=1;}
    private:
        int m_innerInt;
    public:
        void DisplayIn(){cout<<m_innerInt<<endl;}
    } ;
    //End   
public:
    void DisplayOut(){cout<<m_outerInt<<endl;}
};

int main()
{
    Outer out;
    Outer::Inner in;
    out.DisplayOut();
    in.DisplayIn();

    return 0;
}

上記のコードに示すように、この場合、外部クラスと内部クラスは実際にはあまり関連がなく、外部クラスは内部クラス名の役割ドメイン範囲を限定しているにすぎず、Outer限定を加えて他のクラスのように内部クラスを使用することができ、OuterはInnerでは単なるネーミングスペースである.
質問:上記のコードでは、DisplayInなどの内部クラスメンバー関数が外部クラスデータメンバーにどのようにアクセスしますか?
答え:この質問をする前に、まず一つの事実を理解しなければなりません.将来、あなたはInnerインスタンスオブジェクトでInnerのメンバー関数を呼び出します.いわゆる「外部クラスデータメンバーへのアクセス」という言い方は合理的ではありません.「外部クラス」と任意のクラスは、コードにすぎません.メモリの観点から言えば、プログラムが実行された後、コードはコード領域に格納されているので、「外部クラスインスタンスのデータ・メンバーにアクセスする方法」を尋ねる必要があります.これにより、アクセスについて話す前に、外部クラスインスタンス(またはインスタンスのポインタ)が必要になります.
一歩下がると、三七二十一にかかわらず、InnerのDisplayInメソッドに直接このような行を加えるとします.
m_outerInt=10;</span></span>

そしてコンパイル、リンクも通過しました(実際には不可能です)、main関数では:
int main()
{
    Outer::Inner in;
    in.DisplayIn();

    return 0;
}

もしあなたが正常に運行できるなら、天理はどこにありますか?DisplayInのm_outerIntはいったいどのインスタンスのデータですか?
だから、このようなでたらめなことを避けるために、文法の面では、コンパイルも通らないということはあり得ません.
質問:上のコードのInnerをOuterの友元クラスに設定した後、問題を解決できますか?
答え:この質問者は最初の質問者の間違いを犯しただけでなく、友元の意味も誤解した.
友元の例:
class Inner;

class Outer
{
public:
    Outer(){m_outerInt=0;}
private:
    int m_outerInt;
public:
    /*//       
    class Inner
    {
    public:
        Inner(){m_innerInt=1;}
    private:
        int m_innerInt;
    public:
        void DisplayIn(){cout<<m_innerInt<<endl;}
    } ;
    //End   */
public:
    void DisplayOut(){cout<<m_outerInt<<endl;}
    friend Inner;
};
class Inner
{
public:
    Inner(){m_innerInt=1;}
private:
    int m_innerInt;
public:
    void DisplayIn(){cout<<m_innerInt<<endl;}
    //       
    void TestFriend(Outer out)
    {
        cout<<"Good Friend:"<<out.m_outerInt<<endl;
    }
} ;

int main()
{
    Outer out;
    out.DisplayOut();
    Inner in;
    in.DisplayIn();
    in.TestFriend(out);
    return 0;
}

内部クラスが友元アクセス効果(インスタンスまたはインスタンスポインタを直接介してインスタンスにアクセスする非公開メンバー)を達成するには、friendとして別途宣言する必要はありません.理由は言うまでもなく、すでに自分の人です.
質問:内部クラスインスタンス(外部クラスのデータメンバー)は、外部クラスインスタンスのメンバーにどのようにアクセスしますか?
次のコードを参照してください.
#include <iostream>
#define METHOD_PROLOGUE(theClass, localClass) \
    theClass* pThis = ((theClass*)((char*)(this) - \
    offsetof(theClass, m_x##localClass))); \

using namespace std;

class Outer
{
public:
    Outer(){m_outerInt=0;}
private:
    int m_outerInt;
public:
    //       
    class Inner
    {
    public:
        Inner(){m_innerInt=1;}
    private:
        int m_innerInt;
    public:
        void DisplayIn(){cout<<m_innerInt<<endl;}
        //               
        void setOut()
        {
            METHOD_PROLOGUE(Outer,Inner);
            pThis->m_outerInt=10;
        }
    } m_xInner;
    //End   
public:
    void DisplayOut(){cout<<m_outerInt<<endl;}
};

int main()
{
    Outer out;
    out.DisplayOut();
    out.m_xInner.setOut();
    out.DisplayOut();
    return 0;
}

main関数を見てください:プログラムがmain関数の最初の文を実行した後、メモリの中に1つのデータブロックがあって、それはoutのデータを保存して、m_xInnerも
データブロックでは、もちろん、&outとthisポインタ(外部クラス)はメモリブロックの開始位置を指し、内部クラスコードのthisポインタはもちろん
m_xInnerの初期メモリ、offsetof(theClass、m_x##localClass)が取得したのはm_xInnerメモリブロックとメモリブロック
先頭アドレス(これがoutのアドレス)の距離(オフセット)、すなわち、内部クラスthis-外部クラスthisの差(バイト単位)は、内部クラス
thisはそれ自体のオフセットを減算するとpThisが得られる.outのアドレスがあれば、基本的に好きなようにすることができますが、なぜchar*が強く回転しているのかについては
go to definition of offsetofは、charに関する変換が実装されていることがわかります.