logo

C++ でのコンストラクターのコピー

前提条件: C++ のコンストラクター

コピーコンストラクター 同じクラスの別のオブジェクトを使用してオブジェクトを初期化するメンバー関数です。簡単に言えば、以前に作成された同じクラスのオブジェクトでオブジェクトを初期化することによってオブジェクトを作成するコンストラクターは、 コピーコンストラクター



コピー コンストラクターは、既存のオブジェクトのメンバーをコピーすることによって、新しく作成されたオブジェクトのメンバーを初期化するために使用されます。

コピー コンストラクターは、同じクラスのオブジェクトへの参照を引数として受け取ります。

Sample(Sample &t) { id=t.id; }>

コピー コンストラクターを通じてオブジェクトのメンバーを初期化するプロセスは、コピー初期化として知られています。



コピー コンストラクターは 1 つのオブジェクトを既存のオブジェクトで初期化し、両方のオブジェクトがメンバーごとのコピー ベースで同じクラスに属するため、これはメンバーごとの初期化とも呼ばれます。

コピー コンストラクターはプログラマによって明示的に定義できます。プログラマがコピー コンストラクターを定義しない場合は、コンパイラーがコピー コンストラクターを定義します。

例:



整数とJavaの比較
コピー コンストラクターの構文と例

コピーコンストラクターの構文

C++




#include> #include> using> namespace> std;> class> student {> >int> rno;> >char> name[50];> >double> fee;> public>:> >student(>int>,>char>[],>double>);> >student(student& t)>// copy constructor> >{> >rno = t.rno;> >strcpy>(name, t.name);> >fee = t.fee;> >}> >void> display();> };> student::student(>int> no,>char> n[],>double> f)> {> >rno = no;> >strcpy>(name, n);> >fee = f;> }> void> student::display()> {> >cout << endl << rno <<>' '> << name <<>' '> << fee;> }> int> main()> {> >student s(1001,>'Manjeet'>, 10000);> >s.display();> >student manjeet(s);>// copy constructor called> >manjeet.display();> >return> 0;> }>

>

>

出力

 1001 Manjeet 10000 1001 Manjeet 10000>

C++




#include> #include> using> namespace> std;> class> student {> >int> rno;> >char> name[50];> >double> fee;> public>:> >student(>int>,>char>[],>double>);> >student(student& t)>// copy constructor (member wise> >// initialization)> >{> >rno = t.rno;> >strcpy>(name, t.name);> >}> >void> display();> >void> disp() { cout << endl << rno <<>' '> << name; }> };> student::student(>int> no,>char> n[],>double> f)> {> >rno = no;> >strcpy>(name, n);> >fee = f;> }> void> student::display()> {> >cout << endl << rno <<>' '> << name <<>' '> << fee;> }> int> main()> {> >student s(1001,>'Manjeet'>, 10000);> >s.display();> >student manjeet(s);>// copy constructor called> >manjeet.disp();> >return> 0;> }>

>

>

出力

 1001 Manjeet 10000 1001 Manjeet>

コピーコンストラクターの特徴

1. コピー コンストラクターは、既存のオブジェクトのメンバーをコピーすることによって、新しく作成されたオブジェクトのメンバーを初期化するために使用されます。

2. コピー コンストラクターは、同じクラスのオブジェクトへの参照を引数として受け取ります。コピー コンストラクターでオブジェクトを値で渡すと、コピー コンストラクター自体が再帰的に呼び出されます。これは、値による受け渡しにはコピーの作成が含まれ、コピーの作成にはコピー コンストラクターの呼び出しが含まれるため、無限ループが発生するために発生します。参照を使用すると、この再帰が回避されます。したがって、無限呼び出しを避けるためにオブジェクトの参照を使用します。

Sample(Sample &t) { id=t.id; }>

3. コピー コンストラクターを通じてオブジェクトのメンバーを初期化するプロセスは、次のように呼ばれます。 コピーの初期化。

4 。コピー コンストラクターは 1 つのオブジェクトを既存のオブジェクトで初期化し、両方のオブジェクトがメンバーごとのコピー ベースで同じクラスに属するため、これはメンバーごとの初期化とも呼ばれます。

5. コピー コンストラクターはプログラマによって明示的に定義できます。プログラマがコピー コンストラクターを定義しない場合は、コンパイラーがコピー コンストラクターを定義します。

例:

C++


タイプスクリプトの日付時刻



// C++ program to demonstrate the working> // of a COPY CONSTRUCTOR> #include> using> namespace> std;> class> Point {> private>:> >int> x, y;> public>:> >Point(>int> x1,>int> y1)> >{> >x = x1;> >y = y1;> >}> >// Copy constructor> >Point(>const> Point& p1)> >{> >x = p1.x;> >y = p1.y;> >}> >int> getX() {>return> x; }> >int> getY() {>return> y; }> };> int> main()> {> >Point p1(10, 15);>// Normal constructor is called here> >Point p2 = p1;>// Copy constructor is called here> >// Let us access values assigned by constructors> >cout <<>'p1.x = '> << p1.getX()> ><<>', p1.y = '> << p1.getY();> >cout <<>' p2.x = '> << p2.getX()> ><<>', p2.y = '> << p2.getY();> >return> 0;> }>

>

>

出力

p1.x = 10, p1.y = 15 p2.x = 10, p2.y = 15>

コピー コンストラクターの種類

1. デフォルトのコピーコンストラクター

暗黙的に定義されたコピー コンストラクターは、コンストラクターがオブジェクトのベースとメンバーを初期化するのと同じ順序でオブジェクトのベースとメンバーをコピーします。

C++




// Implicit copy constructor Calling> #include> using> namespace> std;> class> Sample {> >int> id;> public>:> >void> init(>int> x) { id = x; }> >void> display() { cout << endl <<>'ID='> << id; }> };> int> main()> {> >Sample obj1;> >obj1.init(10);> >obj1.display();> >// Implicit Copy Constructor Calling> >Sample obj2(obj1);>// or obj2=obj1;> >obj2.display();> >return> 0;> }>

>

>

出力

 ID=10 ID=10>

2. ユーザー定義のコピー コンストラクター

ユーザー定義のコピー コンストラクターは、通常、オブジェクトがファイルなどへのポインターまたは共有不可能な参照を所有する場合に必要です。この場合、デストラクターと代入演算子も記述する必要があります。

C++




// Explicitly copy constructor Calling> #include> using> namespace> std;> class> Sample {> >int> id;> public>:> >void> init(>int> x) { id = x; }> >Sample() {}>// default constructor with empty body> >Sample(Sample& t)>// copy constructor> >{> >id = t.id;> >}> >void> display() { cout << endl <<>'ID='> << id; }> };> int> main()> {> >Sample obj1;> >obj1.init(10);> >obj1.display();> >Sample obj2(> >obj1);>// or obj2=obj1; copy constructor called> >obj2.display();> >return> 0;> }>

>

>

出力

 ID=10 ID=10>

C++




// C++ Programt to demonstrate the student details> #include> #include> using> namespace> std;> class> student {> >int> rno;> >string name;> >double> fee;> public>:> >student(>int>, string,>double>);> >student(student& t)>// copy constructor> >{> >rno = t.rno;> >name = t.name;> >fee = t.fee;> >}> >void> display();> };> student::student(>int> no, string n,>double> f)> {> >rno = no;> >name = n;> >fee = f;> }> void> student::display()> {> >cout << endl << rno <<>' '> << name <<>' '> << fee;> }> int> main()> {> >student s(1001,>'Ram'>, 10000);> >s.display();> >student ram(s);>// copy constructor called> >ram.display();> >return> 0;> }>

ナット vs ベッド
>

>

出力

 1001 Ram 10000 1001 Ram 10000>

コピー コンストラクターはいつ呼び出されますか?

C++ では、次の場合にコピー コンストラクターが呼び出されることがあります。

  • クラスのオブジェクトが値によって返される場合。
  • クラスのオブジェクトが引数として値によって (関数に) 渡されるとき。
  • オブジェクトが同じクラスの別のオブジェクトに基づいて構築される場合。
  • コンパイラが一時オブジェクトを生成するとき。

ただし、これらすべてのケースでコピー コンストラクターが呼び出されるという保証はありません。C++ 標準では、特定の場合にコンパイラがコピーを最適化することが許可されているためです。一例としては、 戻り値の最適化 (RVO と呼ばれることもあります)。

コピー省略

copy elision では、コンパイラーは余分なコピーの作成を防ぎ、その結果、スペースが節約され、プログラムの複雑さ (時間とスペースの両方) が改善されます。したがって、コードがより最適化されます。

例:

C++




// C++ program to demonstrate> // the working of copy elision> #include> using> namespace> std;> class> GFG {> public>:> >void> print() { cout <<>' GFG!'>; }> };> int> main()> {> >GFG G;> >for> (>int> i = 0; i <= 2; i++) {> >G.print();> >cout <<>' '>;> >}> >return> 0;> }>

>

>

出力

 GFG! GFG! GFG!>

ここで、何を出力するかをコンパイラーが決定します。上記の出力を出力することも、以下のケース 1 またはケース 2 を出力することもできます。これが次のとおりです。 戻り値の最適化 は。簡単に言うと、 RVO これは、作成された一時オブジェクトを終了する追加の権限をコンパイラーに与える技術であり、その結果、最終的なプログラムの観察可能な動作/特性が変更されます。

ケース 1:

GFG! GFG!>

ケース 2:

GFG!>

ユーザー定義のコピー コンストラクターが必要になるのはどのような場合ですか?

独自のコピー コンストラクターを定義しない場合、C++ コンパイラーは、オブジェクト間でメンバーごとのコピーを行うクラスごとにデフォルトのコピー コンストラクターを作成します。コンパイラが作成したコピー コンストラクターは、通常は正常に動作します。オブジェクトにポインターまたはリソースの実行時割り当てがある場合にのみ、独自のコピー コンストラクターを定義する必要があります。 ファイルハンドル 、ネットワーク接続など。

デフォルト コンストラクターは浅いコピーのみを実行します。

C++ の浅いコピー

ディープコピーは、ユーザー定義のコピーコンストラクターを使用した場合のみ可能です。 ユーザー定義のコピー コンストラクターでは、コピーされたオブジェクトのポインター (または参照) が新しいメモリ位置を指していることを確認します。

C++ のディープ コピー

コピーコンストラクターと代入演算子

コピー コンストラクターと代入演算子の主な違いは、コピー コンストラクターは呼び出されるたびに新しいメモリ ストレージを作成するのに対し、代入演算子は新しいメモリ ストレージを作成しないことです。

次の 2 つのステートメントのどちらがコピー コンストラクターを呼び出し、どちらが代入演算子を呼び出しますか?

MyClass t1, t2; MyClass t3 = t1; // ---->(1) t2 = t1; // -----> (2)>>

コピー コンストラクターは、既存のオブジェクトから新しいオブジェクトが作成されるときに、既存のオブジェクトのコピーとして呼び出されます。代入演算子は、すでに初期化されたオブジェクトに別の既存のオブジェクトから新しい値が割り当てられるときに呼び出されます。上記の例では、(1) コピー コンストラクターを呼び出し、(2) 代入演算子を呼び出します。詳細については、こちらを参照してください。

例 – コピーコンストラクターが必要なクラス

以下は、Copy コンストラクターの使用法を示す完全な C++ プログラムです。次の String クラスでは、コピー コンストラクターを作成する必要があります。

例:

C++


Javaの更新



// C++ program to demonstrate the> // Working of Copy constructor> #include> #include> using> namespace> std;> class> String {> private>:> >char>* s;> >int> size;> public>:> >String(>const> char>* str = NULL);>// constructor> >~String() {>delete>[] s; }>// destructor> >String(>const> String&);>// copy constructor> >void> print()> >{> >cout << s << endl;> >}>// Function to print string> >void> change(>const> char>*);>// Function to change> };> // In this the pointer returns the CHAR ARRAY> // in the same sequence of string object but> // with an additional null pointer ' '> String::String(>const> char>* str)> {> >size =>strlen>(str);> >s =>new> char>[size + 1];> >strcpy>(s, str);> }> void> String::change(>const> char>* str)> {> >delete>[] s;> >size =>strlen>(str);> >s =>new> char>[size + 1];> >strcpy>(s, str);> }> String::String(>const> String& old_str)> {> >size = old_str.size;> >s =>new> char>[size + 1];> >strcpy>(s, old_str.s);> }> int> main()> {> >String str1(>'GeeksQuiz'>);> >String str2 = str1;> >str1.print();>// what is printed ?> >str2.print();> >str2.change(>'techcodeview.com'>);> >str1.print();>// what is printed now ?> >str2.print();> >return> 0;> }>

>

>

出力

GeeksQuiz GeeksQuiz GeeksQuiz techcodeview.com>

上記のコードからコピー コンストラクターを削除すると、何が問題になるでしょうか?

上記のプログラムからコピー コンストラクターを削除すると、期待した出力が得られません。 str2 に加えられた変更は str1 にも反映されますが、これは予想外です。

C++




#include> #include> using> namespace> std;> class> String {> private>:> >char>* s;> >int> size;> public>:> >String(>const> char>* str = NULL);>// constructor> >~String() {>delete>[] s; }>// destructor> >void> print() { cout << s << endl; }> >void> change(>const> char>*);>// Function to change> };> String::String(>const> char>* str)> {> >size =>strlen>(str);> >s =>new> char>[size + 1];> >strcpy>(s, str);> }> // In this the pointer returns the CHAR ARRAY> // in the same sequence of string object but> // with an additional null pointer ' '> void> String::change(>const> char>* str) {>strcpy>(s, str); }> int> main()> {> >String str1(>'GeeksQuiz'>);> >String str2 = str1;> >str1.print();>// what is printed ?> >str2.print();> >str2.change(>'techcodeview.com'>);> >str1.print();>// what is printed now ?> >str2.print();> >return> 0;> }>

>

>

出力:

GeeksQuiz GeeksQuiz techcodeview.com techcodeview.com>

コピーコンストラクターをプライベートにできますか?

はい、 コピー コンストラクターはプライベートにすることができます。クラス内でコピー コンストラクターをプライベートにすると、そのクラスのオブジェクトはコピーできなくなります。これは、クラスにポインターや動的に割り当てられたリソースがある場合に特に便利です。このような状況では、上記の String の例のように独自のコピー コンストラクターを作成するか、実行時にユーザーが驚くのではなくコンパイラ エラーが発生するようにプライベート コピー コンストラクターを作成することができます。

コピー コンストラクターへの引数を参照として渡す必要があるのはなぜですか?

コピー コンストラクターは、オブジェクトが値によって渡されるときに呼び出されます。コピーコンストラクター自体は関数です。したがって、コピー コンストラクターで値によって引数を渡すと、コピー コンストラクターを呼び出すためにコピー コンストラクターへの呼び出しが行われ、終了しない呼び出しのチェーンになります。したがって、コンパイラはパラメータを値で渡すことを許可しません。

コピー コンストラクターの引数が const である必要があるのはなぜですか?

合格の理由の一つ 定数 参照は、使用する必要があるということです 定数 オブジェクトが誤って変更されないように、可能な限り C++ で記述してください。これは、参照を次のように渡す十分な理由の 1 つです。 定数 しかし、それにはそれだけではありません。 なぜコピー コンストラクターの引数は const でなければならないのですか?