誰でも次のようなことをすることの違いを説明できますか:
a->height();
と
a.height();
実際に違いはありますか?
最初の例では、a はオブジェクトへのポインターであり、2 番目の例では、それはオブジェクト自体 (またはオブジェクトへの参照) です。最低レベルでは、両者の間に明確な違いはありません。
組み込みの について話していると仮定すると、これは奇妙な質問->
です。「ハンマーとリンゴの違いは何ですか」のように聞こえます。通常、「違い」についての質問は、2 つのバリアントが少なくとも多少互換性があり、両方が同時に適用できる場合に適用されます。そのため、人々はどのバリアントを使用するかを決定する「違い」について尋ねます。ここではそうではありません。
a->height()
とa.height()
有効を同時に持つことはできません。のタイプに応じて、2 つのうちの 1 つだけが有効になりますa
。つまり、使用するバージョンを選択することはできません。->
左側がオブジェクトへのポインターである場合、最初のもの ( 付き) が適用されます。2 つ目 (.
のみ) は、左側がオブジェクト値自体である場合にのみ適用されます。それで、それだけです。
->
は、単項*
との組み合わせの省略形にすぎません。つまり、ポインターが と同等である.
ことを意味します。したがって、より合理的な質問は、との違いに関するものです。そして答えは: 違いはありません (繰り返しますが、ビルトインを検討している限り)a
a->height()
(*a).height()
a->height()
(*a).height()
->
スマートポインタのようないくつかのオブジェクトは両方の表記法をサポートしていることにも言及する価値があります。その場合、最初のオブジェクトはポインターが指すオブジェクトを参照し、2番目のオブジェクトはスマートポインター自体を参照します。
のoperator->
後に関数呼び出しが続くと、返されたポインターが逆参照され、関数が結果のオブジェクトで呼び出されます。要するに、 のa->foo()
省略形です(*a).foo()
。
を定義するクラスへのポインタであるa->foo()
必要がある正解とは別に、不自然な例は何か違うことを伝えるかもしれません:a
foo
struct xC {
struct Member {
void foo(){printf("xC::Member::foo\n");};
};
Member a;
Member* operator->() { return &a; }
// following function doesn't really make sense.
void foo() { printf("xC::foo\n");};
};
int main(){
xC x;
x.foo(); // prints "xC::foo".
x->foo();// prints "xC::Member::foo"
}
スマートポインターと stl イテレーターのコンテキストでは、これらの種類のoperator->
定義がよく見られます。その文脈では、定義はStroustrupのガイドラインに従っており、私の例のように直観に反するものに乱用するのではなく、定義クラスをオブジェクトを「指している」かのように使用できるようにします。
それは基本的に、「a」を宣言した方法に要約されます。もしあなたがそうするなら:
SomeClass a;
「a」自体がオブジェクトであり、「.」を使用します。そのメンバーを参照します。
もしあなたがそうするなら:
SomeClass * a;
a = [some expression that produces a pointer to a SomeClass object];
その場合、「a」はオブジェクトへのポインターであり、それ自体はオブジェクトではありません。次に、「->」表記を使用します。
(上記の規則には潜在的な例外がいくつかありますが、それらはほとんどの人が遭遇するものではありません。)
a
それが何らかのオブジェクトへのポインタであると仮定すると、次のようになります。
これ:
a->height();
次の省略形です。
(*a).height();
単なるオブジェクト (または参照) ではなく、ポインターを介してメソッドを呼び出すことができます。
-> バージョンを実際に使用する必要はありませんが、いくつかの呼び出しを連鎖させると、なぜそれが役立つのかが明らかになります。
person->body()->arm(right)->hand()->finger(index)->ring()->activate();
この長い手を書くと、次のようになります。
(*(*(*(*(*(*person).body()).arm(right)).hand()).finger(index)).ring()).activate();
*
逆参照( )を逆参照しているポインターに関連付けるのは難しいです。最初のバージョン -> 左側のものはポインターです。右側のものは、ポインターが逆参照された後のオブジェクトの一部です。
他の人が言っているように、ポインタの間接参照について話している場合、その違いはそれほど重要ではありません。ただし、さらに重要なのは、a
がインスタンスまたは参照であり、operator->
関数を定義している場合、非常に異なる動作が見られる可能性があることです。クラスが実行することを決定した他の作業と一緒に他のタイプの関数を呼び出すことができる間a.name()
、aのname()
関数を呼び出します(これはポインタを返す関数呼び出しであるため、'evil'クラスは、有効なものを返す限り、ほとんど何でも実行できますポインタ)。これは主にスマートポインタに使用されますが、言語での保証はありません。a->name()
name()
要するに、コード自体をどれだけ読みやすくしたいか、そしてどれだけ速くしたいかということです。アセンブラの最下位レベルでは、スタックをプッシュ/プルします。高速なコンピューター/MCU を備えた今日の実生活では、それは問題ではありません。それはあなたが使用するプログラミングのスタイルです。どちらの方法でも問題ありませんが、混在させないでください。これにより、他のプログラマーにとって読みにくくなる可能性があります。