ディープコピーとシャローコピーの違いは何ですか?
シャローコピーはできるだけ複製されません。コレクションのシャローコピーは、要素ではなくコレクション構造のコピーです。浅いコピーで、2つのコレクションが個々の要素を共有します。
ディープコピーはすべてを複製します。コレクションのディープコピーは、元のコレクションのすべての要素が複製された2つのコレクションです。
幅対深さ。あなたのオブジェクトをルートノードとする参照のツリーという観点から考えてください。
浅い:
変数AとBは、BがAに割り当てられている場合、異なるメモリ領域を参照します。2つの変数は、同じメモリ領域を参照します。どちらかの内容を後で変更すると、内容が共有されるため、すぐに他の内容に反映されます。
深い:
変数AとBは、Aが指すメモリ領域の値がBが指すメモリ領域にコピーされるときに、BがAに割り当てられるときに、異なるメモリ領域を参照します。どちらかの内容に対するその後の変更は、AまたはBに固有のままです。内容は共有されません。
一言で言えば、それは何が何を指しているかに依存します。浅いコピーでは、オブジェクトBはメモリ内のオブジェクトAの位置を指します。ディープコピーでは、オブジェクトAのメモリ位置にあるすべてのものがオブジェクトBのメモリ位置にコピーされます。
このウィキの記事は素晴らしい図を持っています。
特にiOS開発者向け:
B
がA
の 浅いコピー である場合、プリミティブデータの場合はB = [A assign];
のようになり、オブジェクトの場合はB = [A retain]
のようになります。
BとAは同じメモリ位置を指しています
B
がA
の ディープコピー である場合は、B = [A copy];
のようになります。
BとAは異なるメモリ位置を指しています
BメモリアドレスはAと同じ
BはAと同じ内容です
シャローコピー:あるオブジェクトから別のオブジェクトにメンバー値をコピーします。
ディープコピー:あるオブジェクトから別のオブジェクトにメンバー値をコピーします。
すべてのポインタオブジェクトは複製され、ディープコピーされます。
例:
class String
{
int size;
char* data;
};
String s1("Ace"); // s1.size = 3 s1.data=0x0000F000
String s2 = shallowCopy(s1);
// s2.size =3 s2.data = 0X0000F000
String s3 = deepCopy(s1);
// s3.size =3 s3.data = 0x0000F00F
// (With Ace copied to this location.)
ここでは、短くて分かりやすい答えを見たことがありません。ですから、試してみます。
浅いコピーでは、ソースによって指されたどのオブジェクトもデスティネーションによって指されます(したがって、参照されたオブジェクトはコピーされません)。
ディープコピーでは、コピー元が指すオブジェクトはコピーされ、コピーはコピー先が指すようになります(したがって、参照される各オブジェクトは2つになります)。これはオブジェクトツリーをたどります。
わかりやすくするために、この記事に従うことができます。 https://www.cs.utexas.edu/~scottm/cs307/handouts/depepCopying.htm
シャローコピー:
ディープコピー:
{2つのオブジェクトを想像してみてください。同じタイプ_tのAとB(C++に関して)で、AからBへの浅い/深いコピーを考えています}
シャローコピー: 単にAへの参照のコピーをBに作成します。それをAの住所のコピーとして考えてください。したがって、AとBのアドレスは同じになる、すなわちそれらは同じメモリ位置、すなわちデータ内容を指していることになる。
ディープコピー: Aのすべてのメンバのコピーを作成し、Bの別の場所にメモリを割り当ててから、コピーしたメンバをBに割り当ててディープコピーを実行します。このように、Aが存在しなくなっても、Bはメモリ内で有効です。使用する正しい用語はクローニングです。ここでは、両者はまったく同じですが、まだ違います(つまり、2つの異なるエンティティとしてメモリスペースに格納されている)ことがわかります。クローンラッパーを提供して、包含/除外リストを介してディープコピー中に選択するプロパティを決定できます。 APIを作成するとき、これは非常に一般的な方法です。
あなたはシャローコピーをすることを選ぶことができます ONLY_IF あなたは関係する賭けを理解しています。 C++やCで扱うべきポインタの数が非常に多い場合、オブジェクトの浅いコピーを作成するのは _本当に_ /悪い考えです。
EXAMPLE_OF_DEEP COPY_ たとえば、画像処理とオブジェクト認識を行おうとしている場合、処理領域から「無関係で反復的な動き」を隠す必要があります。画像ポインタを使用している場合は、それらのマスク画像を保存する仕様があるかもしれません。 NOW ...もしあなたがイメージの浅いコピーをするならば、ポインタ参照がスタックから殺されるとき、あなたは参照とそのコピーを失いました、すなわち、ある時点でアクセス違反の実行時エラーがあるでしょう。この場合、あなたが必要としているのはそれを閉じることによるあなたのイメージの深いコピーです。このようにして、将来必要になった場合に備えてマスクを取得できます。
EXAMPLE_OF_SHALLOW_COPY 私はStackOverflowのユーザーと比較して非常に精通していないので、この部分を削除してあなたが明確にすることができるなら良い例を置いて自由に感じなさい。しかし、あなたのプログラムが無限の期間実行されることを知っているならば、すなわち、関数呼び出しでスタックの上で連続的な「プッシュポップ」操作をするならば、私は本当に浅いコピーをすることはお勧めできません。もしあなたが素人や初心者に何かをデモンストレーションしているなら(例えばC/C++チュートリアルのもの)、それはおそらく大丈夫です。しかし、あなたが監視や探知システム、あるいはSonar Tracking Systemのようなアプリケーションを実行しているなら、あなたのオブジェクトを浅くコピーし続けることは遅かれ早かれあなたのプログラムを殺してしまうので、あなたはそうするべきではありません。
char * Source = "Hello, world.";
char * ShallowCopy = Source;
char * DeepCopy = new char(strlen(Source)+1);
strcpy(DeepCopy,Source);
'ShallowCopy'は 'Source'と同じメモリ内の場所を指しています。 'DeepCopy'はメモリ内の別の場所を指していますが、内容は同じです。
シャローコピーとは何ですか?
シャローコピーは、オブジェクトのビット単位のコピーです。元のオブジェクトの値の正確なコピーを持つ新しいオブジェクトが作成されます。オブジェクトのいずれかのフィールドが他のオブジェクトへの参照である場合は、参照アドレスだけがコピーされます。つまり、メモリアドレスだけがコピーされます。
この図では、MainObject1
にはint型のフィールドfield1
、およびContainObject
型のContainObject1
があります。 MainObject1
のシャローコピーを実行すると、コピーされたMainObject2
の値を含み、まだfield2
自体を指しているfield1
でContainObject1
が作成されます。 field1
はプリミティブ型なので、その値はfield2
にコピーされますが、ContainedObject1
はオブジェクトなので、MainObject2
はContainObject1
を指していることに注意してください。そのため、ContainObject1
内のMainObject1
への変更はすべてMainObject2
に反映されます。
これがシャローコピーの場合、ディープコピーとは何でしょうか。
ディープコピーとは
ディープコピーはすべてのフィールドをコピーし、そのフィールドが指す動的に割り当てられたメモリのコピーを作成します。オブジェクトが参照先のオブジェクトと一緒にコピーされると、ディープコピーが発生します。
この図では、MainObject1はint型のフィールドfield1
とContainObject
型のContainObject1
を持っています。 MainObject1
のディープコピーを実行すると、MainObject2
のコピー値を含むfield2
とfield1
のコピー値を含むContainObject2
でContainObject1
が作成されます。 ContainObject1
内のMainObject1
への変更は、MainObject2
には反映されません。
オブジェクト指向プログラミングでは、型はメンバフィールドの集まりを含みます。これらのフィールドは、値によってまたは参照によって(すなわち、値へのポインタによって)格納することができる。
シャローコピーでは、型の新しいインスタンスが作成され、値が新しいインスタンスにコピーされます。参照ポインタも値と同じようにコピーされます。したがって、参照は元のオブジェクトを指しています。参照によって保管されているメンバーに対する変更は、参照されているオブジェクトのコピーが作成されていないため、元のコピーとコピーの両方に表示されます。
ディープコピーでは、値によって格納されているフィールドは以前のようにコピーされますが、参照によって格納されているオブジェクトへのポインタはコピーされません。代わりに、参照されているオブジェクトのディープコピーが作成され、新しいオブジェクトへのポインタが格納されます。それらの参照されているオブジェクトに加えられた変更は、そのオブジェクトの他のコピーには影響しません。
'ShallowCopy'は 'Source'と同じメモリ内の場所を指しています。 'DeepCopy'はメモリ内の別の場所を指していますが、内容は同じです。
シャロークローニング:
定義:「オブジェクトの浅いコピーは「メイン」オブジェクトをコピーしますが、内側のオブジェクトはコピーしません。」カスタムオブジェクト(例:Employee)が単にプリミティブなString型の変数を持つ場合は、Shallow Cloningを使用します。
Employee e = new Employee(2, "john cena");
Employee e2=e.clone();
オーバーライドされたclone()メソッドでsuper.clone();
を返すと、仕事は終わりです。
ディープクローニング :
定義:「シャローコピーとは異なり、ディープコピーはオブジェクトの完全に独立したコピーです。」
Employeeオブジェクトが別のカスタムオブジェクトを保持していることを意味します。
Employee e = new Employee(2, "john cena", new Address(12, "West Newbury", "Massachusetts");
それから、上書きされたclone()メソッドで 'Address'オブジェクトを複製するためのコードを書かなければなりません。それ以外の場合は、Addressオブジェクトは複製されず、複製されたEmployeeオブジェクトのAddressの値を変更するとバグが発生します。これは元のものも反映します。
var source = { firstName="Jane", lastname="Jones" };
var shallow = ShallowCopyOf(source);
var deep = DeepCopyOf(source);
source.lastName = "Smith";
WriteLine(source.lastName); // prints Smith
WriteLine(shallow.lastName); // prints Smith
WriteLine(deep.lastName); // prints Jones
シャローコピー - 元のオブジェクトおよびシャローコピーされたオブジェクト内の参照変数は、 common objectを参照しています。
ディープコピー - オリジナルおよびディープコピーされたオブジェクト内の参照変数は、 異なる オブジェクトを参照しています。
クローンは常に浅いコピーをします。
public class Language implements Cloneable{
String name;
public Language(String name){
this.name=name;
}
public String getName() {
return name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
メインクラスはフォローしています -
public static void main(String args[]) throws ClassNotFoundException, CloneNotSupportedException{
ArrayList<Language> list=new ArrayList<Language>();
list.add(new Language("C"));
list.add(new Language("Java"));
ArrayList<Language> shallow=(ArrayList<Language>) list.clone();
//We used here clone since this always shallow copied.
System.out.println(list==shallow);
for(int i=0;i<list.size();i++)
System.out.println(list.get(i)==shallow.get(i));//true
ArrayList<Language> deep=new ArrayList<Language>();
for(Language language:list){
deep.add((Language) language.clone());
}
System.out.println(list==deep);
for(int i=0;i<list.size();i++)
System.out.println(list.get(i)==deep.get(i));//false
}
上記のOutPutは
false true true
false false false
元のオブジェクトに加えられた変更は、深いオブジェクトではなく浅いオブジェクトに反映されます。
list.get(0).name="ViSuaLBaSiC";
System.out.println(shallow.get(0).getName()+" "+deep.get(0).getName());
OutPut- / ViSuaLBaSiC C
ディープコピー
ディープコピーはすべてのフィールドをコピーし、そのフィールドが指す動的に割り当てられたメモリのコピーを作成します。オブジェクトが参照先のオブジェクトと一緒にコピーされると、ディープコピーが発生します。
シャローコピー
シャローコピーは、オブジェクトのビット単位のコピーです。元のオブジェクトの値の正確なコピーを持つ新しいオブジェクトが作成されます。オブジェクトのいずれかのフィールドが他のオブジェクトへの参照である場合、参照アドレスだけがコピーされます。つまり、メモリアドレスだけがコピーされます。
正式な定義ではなく例を挙げたい。
var originalObject = {
a : 1,
b : 2,
c : 3,
};
このコードは、 シャローコピー :を示しています。
var copyObject1 = originalObject;
console.log(copyObject1.a); // it will print 1
console.log(originalObject.a); // it will also print 1
copyObject1.a = 4;
console.log(copyObject1.a); //now it will print 4
console.log(originalObject.a); // now it will also print 4
var copyObject2 = Object.assign({}, originalObject);
console.log(copyObject2.a); // it will print 1
console.log(originalObject.a); // it will also print 1
copyObject2.a = 4;
console.log(copyObject2.a); // now it will print 4
console.log(originalObject.a); // now it will print 1
このコードは ディープコピー :を示しています。
var copyObject2 = Object.assign({}, originalObject);
console.log(copyObject2.a); // it will print 1
console.log(originalObject.a); // it will also print 1
copyObject2.a = 4;
console.log(copyObject2.a); // now it will print 4
console.log(originalObject.a); // !! now it will print 1 !!
簡単に言うと、シャローコピーは参照による呼び出しに似ており、ディープコピーは値による呼び出しに似ています。
参照による呼び出しでは、関数の仮パラメータと実パラメータの両方が同じメモリ位置と値を参照します。
値による呼び出しでは、関数の仮パラメータと実パラメータの両方が、異なるメモリ位置を参照しますが、同じ値を持ちます。
Arr1とarr2という2つの配列があるとします。
arr1 = arr2; //shallow copy
arr1 = arr2.clone(); //deep copy
struct sample
{
char * ptr;
}
void shallowcpy(sample & dest, sample & src)
{
dest.ptr=src.ptr;
}
void deepcpy(sample & dest, sample & src)
{
dest.ptr=malloc(strlen(src.ptr)+1);
memcpy(dest.ptr,src.ptr);
}
シャローコピーは、新しい複合オブジェクトを作成し、その参照を元のオブジェクトに挿入します。
シャローコピーとは異なり、deepcopyは新しい複合オブジェクトを作成し、元の複合オブジェクトの元のオブジェクトのコピーも挿入します。
例を見てみましょう。
import copy
x =[1,[2]]
y=copy.copy(x)
z= copy.deepcopy(x)
print(y is z)
上記のコードはFALSEを出力します。
どうぞ見てみましょう。
元の複合オブジェクトx=[1,[2]]
(オブジェクトの中にオブジェクトがあるため複合として呼ばれます(Inception))
あなたが画像で見ることができるように、リストの中にリストがあります。
それからy = copy.copy(x)
を使ってそれの浅いコピーを作成します。 Pythonがここで行うことは、新しい複合オブジェクトを作成しますが、その中のオブジェクトは元のオブジェクトを指しています。
画像の中でそれは外側のリストのための新しいコピーを作成しました。しかし、内側のリストは元のリストと同じままです。
今度はz = copy.deepcopy(x)
を使ってそれのディープコピーを作成します。ここでpythonが何をしているのか、それは外側のリストと内側のリストのための新しいオブジェクトを作成します。下の画像に示すように(赤で強調表示)。
Yとzは同じオブジェクトではないため、コードは最後にFalse
を出力します。
HTH.
他の回答にさらに追加するには、
[ブログ]からの引用: http://sickprogrammersarea.blogspot.in/2014/03/technical-interview-questions-on-c_6.html
ディープコピー は、1つのオブジェクトの内容を使用して同じクラスの別のインスタンスを作成することを含みます。ディープコピーでは、2つのオブジェクトに同じ情報が含まれている可能性がありますが、ターゲットオブジェクトには独自のバッファとリソースがあります。どちらのオブジェクトを破壊しても、残りのオブジェクトには影響しません。オーバーロードされた代入演算子は、オブジェクトのディープコピーを作成します。
シャローコピー は、あるオブジェクトの内容を同じクラスの別のインスタンスにコピーして鏡像を作成します。参照とポインタを直接コピーしているため、2つのオブジェクトは、予測できないように、他のオブジェクトと同じ外部に含まれる内容を共有します。
説明:
コピーコンストラクタを使用して、メンバーごとにデータ値を単純にコピーします。このコピー方法は、シャローコピーと呼ばれます。オブジェクトが組み込み型で構成されポインタを含まない単純なクラスである場合、これは許容されるでしょう。この関数は値とオブジェクトを使用し、その動作は浅いコピーでは変更されず、メンバであるポインタのアドレスだけがコピーされ、アドレスが指している値はコピーされません。その結果、オブジェクトのデータ値は、関数によって誤って変更されることになります。関数が範囲外になると、オブジェクトとそのすべてのデータのコピーがスタックからポップされます。
オブジェクトにポインタがある場合は、ディープコピーを実行する必要があります。オブジェクトのディープコピーでは、メモリがフリーストア内のオブジェクトに割り当てられ、指し示されている要素がコピーされます。関数から返されるオブジェクトにはディープコピーが使用されます。
シャローコピーは新しい参照を作成しませんが、ディープコピーは新しい参照を作成します。
これがディープコピーとシャローコピーを説明するプログラムです。
public class DeepAndShollowCopy {
int id;
String name;
List<String> testlist = new ArrayList<>();
/*
// To performing Shallow Copy
// Note: Here we are not creating any references.
public DeepAndShollowCopy(int id, String name, List<String>testlist)
{
System.out.println("Shallow Copy for Object initialization");
this.id = id;
this.name = name;
this.testlist = testlist;
}
*/
// To performing Deep Copy
// Note: Here we are creating one references( Al arraylist object ).
public DeepAndShollowCopy(int id, String name, List<String> testlist) {
System.out.println("Deep Copy for Object initialization");
this.id = id;
this.name = name;
String item;
List<String> Al = new ArrayList<>();
Iterator<String> itr = testlist.iterator();
while (itr.hasNext()) {
item = itr.next();
Al.add(item);
}
this.testlist = Al;
}
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Oracle");
list.add("C++");
DeepAndShollowCopy copy=new DeepAndShollowCopy(10,"Testing", list);
System.out.println(copy.toString());
}
@Override
public String toString() {
return "DeepAndShollowCopy [id=" + id + ", name=" + name + ", testlist=" + testlist + "]";
}
}
Shallow コピーは新しいオブジェクトを作成し、現在のオブジェクトの非静的フィールドを新しいオブジェクトにコピーします。フィールドが値型の場合 - >フィールドのビットごとのコピーが実行されます。 参照型 - >の場合、参照はコピーされますが、参照されるオブジェクトはコピーされません。したがって、元のオブジェクトとそのクローンは同じオブジェクトを参照します。
Deep copyは新しいオブジェクトを作成してから、現在のオブジェクトの非静的フィールドを新しいオブジェクトにコピーしています。フィールドが 値型の場合 - >フィールドのビットごとのコピーが実行されます。フィールドが 参照型 - >の場合、参照されているオブジェクトの新しいコピーが実行されます。クローンを作成するクラスには[Serializable]というフラグを立てる必要があります。
Ararysをコピーする
Arrayはクラスです。つまり、それは参照型なので、array1 = array2は同じ配列を参照する2つの変数になります。
しかし、この例を見てください。
static void Main()
{
int[] arr1 = new int[] { 1, 2, 3, 4, 5 };
int[] arr2 = new int[] { 6, 7, 8, 9, 0 };
Console.WriteLine(arr1[2] + " " + arr2[2]);
arr2 = arr1;
Console.WriteLine(arr1[2] + " " + arr2[2]);
arr2 = (int[])arr1.Clone();
arr1[2] = 12;
Console.WriteLine(arr1[2] + " " + arr2[2]);
}
シャロークローン は、クローン配列で表されるメモリのみがコピーされることを意味します。
配列が値型オブジェクトを含む場合、値はコピーされます ;
配列が参照型を含む場合、参照のみがコピーされます - その結果、メンバが同じオブジェクトを参照する2つの配列が存在します 。
参照型が重複するディープコピーを作成するには、配列をループ処理して各要素を手動で複製する必要があります。
コピーコンストラクタは、同じクラスの以前に作成されたオブジェクトで新しいオブジェクトを初期化するために使用されます。デフォルトでは、コンパイラは浅いコピーを書きました。動的メモリ割り当てが関係している場合、両方のオブジェクトはヒープ内の同じメモリ位置を指すので、シャローコピーは問題なく動作します。したがって、この問題を解決するために、両方のオブジェクトが独自の属性コピーを持つようにディープコピーを作成しました。メモリに。完全な例と説明と共に詳細を読むために、あなたは記事 C++コンストラクタ を見ることができた。
浅いコピーと混乱のためにもう少しだけ追加して、単純にlistに新しい変数名を割り当てるため。
「我々が持っていると言う:
x = [
[1,2,3],
[4,5,6],
]
このステートメントは、2つの内側リストと1つの外側リストの3つのリストを作成します。外部リストへの参照はxという名前で利用可能になります。したら
y = x
データはコピーされません。まだどこかに同じ3つのリストがメモリに残っています。こうすることで、外側のリストを、以前の名前xに加えて、名前yで使用できるようになります。したら
y = list(x)
または
y = x[:]
これはxと同じ内容の新しいリストを作成します。リストxは2つの内部リストへの参照を含んでいたので、新しいリストはそれらの同じ2つの内部リストへの参照も含むでしょう。コピーされるリストは1つだけです。外側のリストです。これで、メモリ内に4つのリスト、2つの内部リスト、外部リスト、および外部リストのコピーがあります。元の外部リストはxという名前で利用でき、新しい外部リストはyという名前で利用できるようになります。
内側のリストはコピーされていません!この時点でxまたはyのどちらからでも内部リストにアクセスして編集できます。
2次元(またはそれ以上)のリスト、あるいはあらゆる種類のネストされたデータ構造を持っていて、すべてを完全にコピーしたい場合は、コピーモジュールでdeepcopy()関数を使用します。あなたの解決策はまた、外側のリストの項目を反復してそれらのそれぞれのコピーを作成し、そしてすべての内側のコピーのために新しい外側のリストを構築するので、2-Dリストに対しても働く。」
私は以下の行から理解するようになりました。
シャローコピーはobject value type(int、float、bool)フィールドをターゲットオブジェクトにコピーし、オブジェクトの参照型(string、classなど)はターゲットオブジェクトのreferencesとしてコピーされます。このターゲットでは、参照型はソースオブジェクトのメモリ位置を指しています。
ディープコピーは、オブジェクトの値と参照型をターゲットオブジェクトの完全な新しいコピーにコピーします。つまり、値型と参照型の両方に新しいメモリ位置が割り当てられます。
上記の定義すべてに加えて、もう1つの最も一般的に使用されるディープコピーは、クラスのコピーコンストラクタ(またはオーバーロード代入演算子)にあります。
シャローコピー - >は、コピーコンストラクタを提供していない場合です。ここでは、オブジェクトだけがコピーされ、クラスのすべてのメンバーがコピーされるわけではありません。
ディープコピー - >は、あなたが自分のクラスにコピーコンストラクタやオーバーロード代入を実装することを決定したときで、クラスのすべてのメンバをコピーすることを可能にします。
MyClass& MyClass(const MyClass& obj) // copy constructor for MyClass
{
// write your code, to copy all the members and return the new object
}
MyClass& operator=(const MyClass& obj) // overloading assignment operator,
{
// write your code, to copy all the members and return the new object
}