Tips8 クラスメンバへのポインタ

前のTips 次のTips


■ クラスメンバへのポインタ

 あまり知られていないようですが、C++ではクラスメンバへのポインタを使用することが できます。この仕様は初期のC++にはなかった仕様で、VC++ではいろいろと使用上の制限が ありますが、なかなか便利なものです。
 『クラスメンバへのポインタ』とは若干誤解をまねきやすい用語ですが、クラスのオブジェクト の先頭アドレスから指定メンバのアドレスの相対値と考えればわかりやすいので、 『メンバのオフセット』の方が用語として適切かもしれません。

クラスオブジェクト
 ┏━━━━━━┓───
 ┃      ┃ ↑
 ┃      ┃ │ メンバAのオフセット
 ┃      ┃ │   ≡ メンバAへのポインタ
 ┃      ┃ ↓
 ┠──────┨───
 ┃ メンバA ┃
 ┠──────┨
 ┃      ┃
 ┃      ┃
 ┃      ┃
 ┗━━━━━━┛

 メンバへのポインタはあくまで、クラスメンバへのポインタであって、インスタンスのメンバ へのポインタではありません。したがって、通常のポインタのように実際のアドレスを指している のではなく、より抽象的なものです。

 『&クラス名::メンバ名』でメンバへのポインタを取得できます。その型はメンバの型を T と するとき、『T クラス名::*』となります。
 例えば、クラス C1 が int 型のメンバ m_int1, m_int2 を持っている時、

        int C1::*ptr = &C1::m_int1;
        int C1::*ptr2 = &C1::m_int2;

のようにしてメンバへのポインタを取得します。

 メンバへのポインタを参照するときは、クラスのオブジェクトに対し『.*』または『->*』演算子 を使います。例えば、先の C1 クラスのオブジェクト obj があれば、

        C1 obj;
        obj.*ptr = 123;

で、ptr が指すメンバに値を設定することが出来ます。

注意:

■ メンバへのポインタの使用例

 メンバへのポインタの使用が有効な例のひとつが2つのクラスオブジェクトでメンバを相互に コピーする場合です。例えば、MFCのダイアログはチェックボックスやエディットコントロールの 文字列等をパブリックなメンバ変数にもつのが普通で、ダイアログを表示する前にそれらを アプリケーションオブジェクトからコピーし、ダイアログでOKが押されると、それらを再び アプリケーションオブジェクトにコピーするというコードはよくあります。

        CMyDialog aDlg;
        aDlg.m_aaa = m_aaa;
        aDlg.m_bbb = m_bbb;
        .....
        aDlg.m_zzz = m_zzz;
        if( aDlg.DoModal() == IDOK ) {
            m_aaa = aDlg.m_aaa;
            m_bbb = aDlg.m_bbb;
            .....
            m_zzz = aDlg.m_zzz;
            .....
        }

 しかし、このようなコードはやり取りするメンバの情報が分散しているという点で好ましく ありません。やりとりするメンバを修正するためにコードを2個所修正しなくてはならず、 バグの原因になりかねません。

 このような状況ではメンバへのポインタを使うとうまく行きます。データをやり取りする メンバをテーブルにし、データのコピーはそのテーブルを参照するようにします。

    struct STableItem {
        int     CMyDialog::*m_dlgMember;        //  ダイアログクラスのメンバへのポインタ
        int     CMyApp::*m_appMember;           //  CMyApp クラスのメンバへのポインタ
    };
    static STableItem table[] = {               //  データを交換するメンバの一覧
        {&CMyDialog::m_aaa, &CMyApp::m_aaa},
        {&CMyDialog::m_bbb, &CMyApp::m_bbb},
        .....
        {&CMyDialog::m_zzz, &CMyApp::m_zzz},
    };
    
        CMyDialog aDlg;
        int itemsCount = sizeof(table)/sizeof(STableItem);
        for(int i=0;i<itemsCount;++i)
            aDlg.*table[i].m_dlgMember = theApp.*table[i].m_appMembe;
        if( aDlg.DoModal() == IDOK ) {
            for(int i=0;i<itemsCount;++i)
                theApp.*table[i].m_appMembe = aDlg.*table[i].m_dlgMember;
            .....
        }

 どうです。スッキリ書けたでしょう。(えっ、最初の方がスッキリしているって?ふっ、甘いな。)


前のTips 次のTips 津田伸秀 のホームページに戻る。

Last Updated on 8-Sep-1996, Copyright (c) 1996 by Nobuhide Tsuda, All Right Reserved.
このホームページに関するご質問、ご要望、バグレポート等は  Nobuhide_Tsuda@jsn.justnet.or.jp  までメールをいただければ幸いです。