C++コピーコンストラクタと=代入演算子の詳細
3801 ワード
まず、次の点を明確にします.
デフォルトのコピーコンストラクタと=コピー演算子が用意されています.いわゆる浅いコピーです.
しかし、時には、私たちは自分で書き直さなければなりません.一般的にはポインタがある場合に書き換えます.
簡単な例を挙げると、ポインタはありません.実際には書き換える必要はありません.ただ、プレゼンテーションのためです.
output:
ポインタがある場合は、次のように深くコピーする必要があります.
同じクラスの別のオブジェクトのステータスをコピーすることで、オブジェクトを初期化します.
使用:オブジェクトを数値で渡すか、数値で返すか、明示的にコピーします.
代入演算子は何を返しますか:一般的には参照によって*thisを返します.つまり、ユーザーが定義したクラスの代入値は内部タイプと同じ約束を守り、代入値も式として使用できます.つまり、カスケードすることができます.
自己付与文法には間違いはありませんが、付与演算子をうまく実現していない場合は、災害が待っている可能性があります.
自己付与が無害であることを確認しなければなりません
保証付与演算子には、完全に成功し、オブジェクトをそのまま残し、例外を投げ出す2つの結果しかありません.
派生クラス割り当て演算子の使用
派生クラスの賦値演算子は、まずその直接ベースクラス賦値演算子(ベースクラスに宣言されたメンバーオブジェクトに値を付与)を呼び出し、次にそのメンバーオブジェクトの賦値演算子(派生クラスで宣言されたメンバーオブジェクトを変更する)を呼び出します.これらの賦値は、通常、ベースクラスとメンバーオブジェクトがそのクラスの定義に現れる順序と同じです.
代入演算子の正しい形式を再ロードします.
c++のデザイナーstroustrupは、ユーザーがタイプをカスタマイズしてできるだけ内部タイプの働き方と似ているように工夫しています.そのために彼はたくさんの努力をした.(演算子のリロード、タイプ変換関数の書き込み、構造関数のコピーなど)を実行します.また、続行する必要があります.値の割り当てを見てみましょう.内部タイプの場合、int w,x,y,z;w=x=y=z=0のように、値の割り当て操作をチェーンすることができます.したがって、ユーザー定義タイプの値の割り当て操作をチェーンすることもできます:CString w,x,y, z;//MFC「カスタマイズ」のタイプw=x=y=z=「hello」付与演算子の結合性は生まれながらにして右から左になるため、上の付与値はw=(x=(y=(z=「hello」)と解析することができる. <=> w.operator=(x.operator=(y.operator=(z.operator=("hello")))); このフォーマットは、w.operator=、x.operator=およびy.operator=のパラメータが、前のoperator=呼び出しの戻り値であることを示します.したがってoperator=の戻り値は、入力パラメータとして関数自身に受け入れられる必要があります.一般的にoperator=入力はクラスオブジェクトまたはクラスオブジェクトの参照であるべきであり、効率的には後者は前者より優れているため、入力と返されるのはクラスオブジェクトの参照であるべきである.またint x,y,zがあるから; (x = y) = z ; したがって、次の文も正しいCString x,y,zであるべきである. ( x = y) = z; ではoperator=の戻りはconst(定数が左に与えられないため)ではなく、CString xがあります. x = “Hello”; <=> const CString temp(“Hello”);//一時オブジェクトが生成され、なぜconst x=tempなのか.したがって、上記の文が成立することを保証するために、operator=の入力はconstであるべきであるため、最も良い実現はT&operator=(const T&);
デフォルトのコピーコンストラクタと=コピー演算子が用意されています.いわゆる浅いコピーです.
しかし、時には、私たちは自分で書き直さなければなりません.一般的にはポインタがある場合に書き換えます.
簡単な例を挙げると、ポインタはありません.実際には書き換える必要はありません.ただ、プレゼンテーションのためです.
class Fraction{
private:
int fenmu; //
int fenzi; //
public:
Fraction(int x,int y){
fenzi = x;
fenmu = y;
}
Fraction(){}
Fraction(const Fraction & fr);
Fraction & operator=(Fraction& p);
void display(){
cout << fenmu << " " << fenzi;
}
};
Fraction::Fraction(const Fraction & fr){
cout << "test: use copy" << endl;
fenmu = fr.fenmu;
fenzi = fr.fenzi;
}
Fraction & Fraction::operator=(Fraction& fr){
if(this == &fr)
return *this;
fenmu = fr.fenmu;
fenzi = fr.fenzi;
cout << "test use =" << endl;
return *this;
}
int main(){
Fraction f(1,2);
Fraction f2(f); //use copy
//f2.display();
Fraction f3 = f2; // use copy
Fraction f4,f5;
f5 = f4 = f3; // use =
//f5.display();
return 0;
}
output:
test: use copy
test: use copy
test use =
test use =
ポインタがある場合は、次のように深くコピーする必要があります.
#include <iostream>
using namespace std;
class CA
{
public:
CA(int b,char* cstr)
{
a=b;
str=new char[b];
strcpy(str,cstr);
}
CA(const CA& C)
{
a=C.a;
str=new char[a]; //
if(str!=0)
strcpy(str,C.str);
}
void Show()
{
cout<<str<<endl;
}
~CA()
{
delete str;
}
private:
int a;
char *str;
};
int main()
{
CA A(10,"Hello!");
CA B=A;
B.Show();
return 0;
}
同じクラスの別のオブジェクトのステータスをコピーすることで、オブジェクトを初期化します.
使用:オブジェクトを数値で渡すか、数値で返すか、明示的にコピーします.
代入演算子は何を返しますか:一般的には参照によって*thisを返します.つまり、ユーザーが定義したクラスの代入値は内部タイプと同じ約束を守り、代入値も式として使用できます.つまり、カスケードすることができます.
自己付与文法には間違いはありませんが、付与演算子をうまく実現していない場合は、災害が待っている可能性があります.
自己付与が無害であることを確認しなければなりません
CAssignment& CAssignment::operator=(const CAssignment& a)
{
if( this == &a )
return *this;
//….
}
保証付与演算子には、完全に成功し、オブジェクトをそのまま残し、例外を投げ出す2つの結果しかありません.
CAssignment& CAssignment::operator=(const CAssignment& a)
{
if( this == &a )
return *this;
CTemp* t = new CTemp;
//…..
delete _tmp;
_tmp = t;
return *this;
}
派生クラス割り当て演算子の使用
派生クラスの賦値演算子は、まずその直接ベースクラス賦値演算子(ベースクラスに宣言されたメンバーオブジェクトに値を付与)を呼び出し、次にそのメンバーオブジェクトの賦値演算子(派生クラスで宣言されたメンバーオブジェクトを変更する)を呼び出します.これらの賦値は、通常、ベースクラスとメンバーオブジェクトがそのクラスの定義に現れる順序と同じです.
CDerived& CDerived::operator=(const CDerived& r)
{
CBase::operator=(r);
_c = r._c;
return *this;
}
代入演算子の正しい形式を再ロードします.
c++のデザイナーstroustrupは、ユーザーがタイプをカスタマイズしてできるだけ内部タイプの働き方と似ているように工夫しています.そのために彼はたくさんの努力をした.(演算子のリロード、タイプ変換関数の書き込み、構造関数のコピーなど)を実行します.また、続行する必要があります.値の割り当てを見てみましょう.内部タイプの場合、int w,x,y,z;w=x=y=z=0のように、値の割り当て操作をチェーンすることができます.したがって、ユーザー定義タイプの値の割り当て操作をチェーンすることもできます:CString w,x,y, z;//MFC「カスタマイズ」のタイプw=x=y=z=「hello」付与演算子の結合性は生まれながらにして右から左になるため、上の付与値はw=(x=(y=(z=「hello」)と解析することができる. <=> w.operator=(x.operator=(y.operator=(z.operator=("hello")))); このフォーマットは、w.operator=、x.operator=およびy.operator=のパラメータが、前のoperator=呼び出しの戻り値であることを示します.したがってoperator=の戻り値は、入力パラメータとして関数自身に受け入れられる必要があります.一般的にoperator=入力はクラスオブジェクトまたはクラスオブジェクトの参照であるべきであり、効率的には後者は前者より優れているため、入力と返されるのはクラスオブジェクトの参照であるべきである.またint x,y,zがあるから; (x = y) = z ; したがって、次の文も正しいCString x,y,zであるべきである. ( x = y) = z; ではoperator=の戻りはconst(定数が左に与えられないため)ではなく、CString xがあります. x = “Hello”; <=> const CString temp(“Hello”);//一時オブジェクトが生成され、なぜconst x=tempなのか.したがって、上記の文が成立することを保証するために、operator=の入力はconstであるべきであるため、最も良い実現はT&operator=(const T&);