実際、Java は同じように動作します。説明させてください:
Object obj = new Object();
List<Object> list = new LinkedList<Object>();
list.add(obj);
の型はobj
何ですか? への参照Object
です。実際のオブジェクトはヒープのどこかに浮かんでいます。Java でできることは、オブジェクトへの参照を渡すことだけです。オブジェクトへの参照をリストのadd
メソッドに渡すと、リストはその参照のコピーをそれ自体に格納します。obj
リストに保存されているその参照の別のコピーに影響を与えることなく、後で名前付き参照を変更できます。(もちろん、オブジェクト自体を変更すると、どちらの参照でもその変更を確認できます。)
C++ にはさらに多くのオプションがあります。Java をエミュレートできます。
class Object {};
// ...
Object* obj = new Object;
std::list<Object*> list;
list.push_back(obj);
の型はobj
何ですか? へのポインタObject
です。push_back
リストのメソッドに渡すと、リストはそのポインターのコピーをそれ自体に格納します。これは、Java と同じセマンティクスを持っています。
しかし、効率の観点から考えてみると、C++ ポインター/Java 参照はどれくらい大きいのでしょうか? アーキテクチャに応じて、4 バイトまたは 8 バイト。関心のあるオブジェクトがそのサイズ以下のサイズである場合、なぜそれをヒープに配置してから、そのオブジェクトへのポインターをどこにでも渡す必要があるのでしょうか? オブジェクトを渡すだけです:
class Object {};
// ...
Object obj;
std::list<Object> list;
list.push_back(obj);
さて、obj
実物です。それをリストのメソッドに渡します。このメソッドは、そのオブジェクトのコピーpush_back
をそれ自体に格納します。これは、ある意味で C++ のイディオムです。ポインターが純粋なオーバーヘッドである小さなオブジェクトにとって意味があるだけでなく、非 GC 言語 (偶発的にリークされる可能性のあるヒープには何もない) での作業も容易になり、オブジェクトの寿命が自然に結び付けられている場合リストに追加する場合 (つまり、リストから削除された場合、意味的には存在しなくなるはずです)、オブジェクト全体をリストに保存することもできます。また、キャッシュの局所性の利点もあります (とにかくで使用する場合)。std::vector
「では、なぜpush_back
参照引数を取るのですか?」と疑問に思うかもしれません。それには十分に単純な理由があります。すべてのパラメーターは値で渡されます (これも C++ と Java の両方で)。std::list
ofがある場合はObject*
、問題ありません。ポインターを渡すと、そのポインターのコピーが作成され、関数に渡されpush_back
ます。次に、その関数内で、そのポインターの別のコピーが作成され、コンテナーに格納されます。
ポインタでいいです。しかし、C++ では、オブジェクトのコピーは任意に複雑になる可能性があります。コピー コンストラクターは何でもできます。状況によっては、オブジェクトを 2 回 (1 回は関数に、もう 1 回はコンテナーに) コピーすると、パフォーマンスの問題が発生する可能性があります。push_back
そのため、その引数は const 参照によって取得されます。つまり、元のオブジェクトから直接コンテナーに単一のコピーが作成されます。