Cでは、a->b
はと正確に同等(*a).b
です。「矢印」表記は、便宜上導入されました。ポインタを介して構造体のメンバーにアクセスすることはかなり一般的であり、矢印表記は書き込み/入力が簡単であり、一般的にも読みやすいと考えられています。
ただし、C++では別のしわも追加されます。/operator->
に対してオーバーロードされる可能性があります。それ以外はかなり珍しいことですが、そうすることはスマートポインタクラスでは一般的です(ほぼ必須です)。struct
class
これ自体はそれほど珍しいことではありません。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=
するため、割り当てからの出力も確認できます)。