株式会社エス・スリー・フォー

[Tools.h++]10.2 コレクションクラスのコピー

クラスのコピーは、コピーコンストラクタが適用される度に、また、プロセスが作業するコピーを必要とする度に、一般的に行われています。値ベースコレクションクラスのコピーは単純ですが、参照ベースコレクションクラスのコピーには特別な配慮が必要です。ここでは、それについて説明します。

10.2.1 参照ベースコレクションクラスのコピー

参照ベースコレクションクラスのコピー、または別のオブジェクトを参照するクラスのコピーを作成すると、何が起こるのでしょうか? これは、2 つの手段−「浅いコピー」または「深いコピー」のどちらを選択するかにより異なります。

  1. オブジェクトの浅いコピーは、元のオブジェクトと同一のインスタンス変数を持つ新しいオブジェクトを作成します。例えば、Set の浅いコピーは、元の Set と同じメンバ関数を持ち、ポインタを介して元の Set とオブジェクトを共有します。浅いコピーは「参照の意味論」を使う、と言われることもあります。


    すべての Rogue Wave の参照ベースコレクションクラスのコピーコンストラクタは、浅いコピーを作成します。


  2. オブジェクトの深いコピーは、まったく新しいインスタンス変数を持つ新しいオブジェクトを作成します。元のオブジェクトとはオブジェクトを共有しません。例えば、Set の深いコピーは、新しい Set を作成するだけでなく、元の項目もコピーして挿入します。真の深いコピーでは、コピー作業は再帰的に行われます。深いコピーは「値の意味論」を使う、と言われることもあります。

参照ベースのコレクションクラスの中には、まったく新しいインスタンス変数を持つ新しいオブジェクトを返すメンバ関数 copyContentsTo() を持つものがあることに注意してください。このコピー作業は再帰的には行われず、新しいインスタンス変数は元のインスタンス変数の浅いコピーです。

次に、浅いコピーと深いコピーの違いを図解します。コピー前の Bag (重複を許可し、順序性がないオブジェクトのコレクションクラス) が、次のようであるとします。

image1

Bag の浅いコピーと深いコピーは、それぞれ次のような結果をもたらします。

image2

深いコピーは Bag そのものだけでなく、その中に含まれるオブジェクトすべてを再帰的にコピーすることがわかります。

どちらの手段でコピーするかは、重要な違いです。例えば、浅いコピーはコピー量が少ないため便利で処理も速くなりますが、コピー後 2 つのコレクションが同じオブジェクトを参照するため、使い方には十分注意が必要です。一方のコレクションで項目をすべて削除すると、他のコレクションが意味のないものを指していることになるからです。

オブジェクトをディスクに書き込むときにも、どちらの手段を取るかを考慮する必要があります。オブジェクトに、同じオブジェクトを指すポインタまたは参照が 2 つ以上含まれている場合、オブジェクトを読み出すときにこの形態 (参照関係) を維持することが大切です。 RWCollectable から派生したクラスは、オブジェクトの形態維持を保証するアルゴリズムを継承します。これについて詳しくは、第 14 章を参照してください。

10.2.2 参照ベースコレクションクラスのコピー

参照ベースコレクションのコピー結果を値ベースコレクションのものと比較してみましょう。次のクラスを例に使います。

RWTValOrderedVector<RWCString>

これは、RWCString を具体化する (型引数に持つ) 順序付きのベクトルテンプレートです。この場合、各文字列はコレクションの中に埋め込まれています。コレクションクラスのコピーが作成されるとき、コレクションクラス自体だけでなく、その中のオブジェクトもコピーされます。この結果、コレクション内のオブジェクトのコピーも別に作成されます。

image3