Cでは、a->bはと正確に同等(*a).bです。「矢印」表記は、便宜上導入されました。ポインタを介して構造体のメンバーにアクセスすることはかなり一般的であり、矢印表記は書き込み/入力が簡単であり、一般的にも読みやすいと考えられています。
ただし、C++では別のしわも追加されます。/operator->に対してオーバーロードされる可能性があります。それ以外はかなり珍しいことですが、そうすることはスマートポインタクラスでは一般的です(ほぼ必須です)。structclass
これ自体はそれほど珍しいことではありません。C++では、演算子の大部分をオーバーロードできます(ただし、、など、ほとんどの場合、オーバーロードする必要はありません)。operator&&operator||operator,
異常なのは、オーバーロードがどのようにoperator->解釈されるかです。まず、二項演算子のa->bように見えます->が、C ++でオーバーロードすると、単項演算子として扱われるため、正しい署名はT::operator()、ではなくT::operator(U)、その順序の何かになります。
結果もやや異常に解釈されます。fooをオーバーロードするあるタイプのオブジェクトであると仮定するとoperator->、foo->barは意味として解釈され(f.operator->())->barます。これにより、オーバーロードされたのリターンタイプが制限されoperator->ます。具体的には、オーバーロードする別のクラスのインスタンスoperator->(またはそのようなオブジェクトへの参照)を返すか、ポインターを返す必要があります。
前者の場合、単純に見えるfoo->barということは、実際には、オブジェクトのインスタンスのチェーン全体(任意に長い)を「追跡」することを意味します。各インスタンスは、operator->という名前のメンバーを参照できる1つに到達するまで、それぞれがオーバーロードしbarます。(明らかに極端な)例として、次のことを考慮してください。
#include <iostream>
class int_proxy {
int val;
public:
int_proxy(): val(0) {}
int_proxy &operator=(int n) {
std::cout<<"int_proxy::operator= called\n";
val=n;
return *this;
}
};
struct fubar {
int_proxy bar;
} instance;
struct h {
fubar *operator->() {
std::cout<<"used h::operator->\n";
return &instance;
}
};
struct g {
h operator->() {
std::cout<<"used g::operator->\n";
return h();
}
};
struct f {
g operator->() {
std::cout<<"Used f::operator->\n";
return g();
}
};
int main() {
f foo;
foo->bar=1;
}
ポインタを介したメンバーへの単純な割り当てのように見えfoo->bar=1;ますが、このプログラムは実際には次の出力を生成します。
Used f::operator->
used g::operator->
used h::operator->
int_proxy::operator= called
明らかに、この場合、単純なと同等でfoo->barはありません(それに近い場合でも)(*foo).bar。出力から明らかなように、コンパイラーは「隠し」コードを生成して->、さまざまなクラスのオーバーロードされた演算子のシリーズ全体をウォークスルーし、名前付きのメンバー(この場合はタイプでもありfooます)を持つもの(へのポインター)に到達しますbarこれはオーバーロードoperator=するため、割り当てからの出力も確認できます)。