アロー演算子のオーバーロードの注意点

アロー演算子オーバーロードして別のオブジェクトのポインタを返す処理があるとします。

以下のような感じです。

#include <iostream>
using std::cout; using std::endl;

class CMember {
public:
    void output () {
        cout << "CMember::output" << endl;
    }
};

class CClass {
private:
    CMember* m_pCmember;
public:
    CClass () {
        m_pCmember = new CMember;
    }
    
    ~CClass () {
        delete m_pCmember;
    }
    
    // アロー演算子をオーバーロードして別のオブジェクトを返す
    CMember* operator->() {
        return m_pCmember;
    }
    
    void output () {
        cout << "CClasss::output" << endl;
    }
};

int main () {
    CClass* obj = new CClass;
    
    obj->output();
    
    delete obj;
    return 0;
}

さてこれを実行した場合、画面に表示されるのはCClass::outputでしょうか?CMember::outputでしょうか?




正解は・・・・

$ main
CClasss::output

CClass::outputの方でした。アロー演算子オーバーロードしているはずなのに何故こうなるんでしょうか。

答えは簡単です。obj変数はCClass型の変数ではなく、CClassのポインタ型だからです。

つまりobj変数は単なるポインタなのでCClassの影響を受けません。なので「obj->output()」におけるアロー演算子は「(*obj).output()」のシンタックスシュガーであり、アロー演算子でoutputを呼び出しているわけでは無いのでCClassの方のoutputが呼び出されたということになります。

なのでもしCMemberのoutputを呼び出したければ

(*obj)->output();

のように一旦間接参照してからアロー演算子で呼び出してやればよいということになります。

少し考えれば当たり前の話なのですが、コレのせいでハマってしまったので自戒を込めて。気をつけましょう。