C++の代入演算子とコピーコンストラクタ
C++の代入演算子とコピーコンストラクタについて注意事項を整理しました。さらに、基本データ型との比較によって代入演算子とコピーコンストラクタについての理解を深めます。
代入演算子
C++で以下のような代入演算を行うと、どうなるでしょうか。
ClassName VariableName; //初期化 VariableName = ObjectName; //代入演算
JavaやJavaScriptであれば、オブジェクトは参照として変数に代入されるので、ObjectNameが指すオブジェクトをVariableNameでも使えるようにするために、上記のような代入演算をよく用いると思います。一方、C++の場合は、上記式の処理は「代入演算子Operator=()が実行され、VariableNameオブジェクトにObjectNameオブジェクトの内容がコピーされる。」ことになり、実際は代入演算子というよりコピー演算子と言えます。つまり、JavaやJavaScriptでは実体の同じオブジェクトの参照を変数間でコピーするのに対して、C++ではVariableNameオブジェクトとObjectNameオブジェクトの実体は異なっていて、その内容がコピーされます。
JavaやJavaScriptのプログラミングに慣れた方は、参照を扱っているつもりで実体の異なるオブジェクトを操作してしまうことになるので気を付ける必要があります。さらに、代入演算子Operator=()はデフォルトで定義されていますが、あくまでもレコードのコピーであり、メンバにポインタがあってもポインタをコピーするだけで、その新しいオブジェクトを作成してくれるわけではありません。この点にも気を付ける必要があります。ちなみに、代入演算子Operator=()は下記のような宣言になります。
void Operator=(const ClassName ©);
C++でオブジェクトの実体を一意に扱いたい場合は、ポインタを使います。「&」演算子で参照を宣言することもできますが、JavaやJavaScriptのように変数内の参照を入れ替えることはできません。参照については、C++の参照の宣言方法、振る舞い、一時オブジェクトによる初期化をご覧ください。
コピーコンストラクタ
ちなみに、冒頭の式について初期化と代入演算を一度に行う場合は、
ClassName VariableName = ObjectName; ClassName VariableName(ObjectName);
とします。どちらも同じ処理で、コピーコンストラクタが呼び出されます。コピーコンストラクタは、
ClassName(const ClassName ©)
と宣言します。こちらもデフォルトで定義されていますが、あくまでもレコードのコピーであることは、代入演算子と同じで気を付ける必要があります。
基本データ型との比較
定義型と基本データ型の初期化・代入の形式を比較してみます。
定義型:
ClassName VariableName; //引数なしの初期化 VariableName = ObjectName; //代入演算 ClassName VariableName = ObjectName; //引数が同じクラスでの初期化、コピーコンストラクタ ClassName VariableName(ObjectName); //引数が同じクラスでの初期化、コピーコンストラクタ ClassName VariableName(AnyArguments); //その他の引数での初期化
基本データ型:
DataName VariableName; //Data(リテラル式、他の変数)なしの初期化 VariableName = Data;//代入演算 DataName VariableName = Data; //Dataでの初期化 DataName VariableName(Data); //Dataでの初期化
比較すると、代入演算子とコピーコンストラクタの役割がはっきりすると思います。そもそもC,C++の基本データ型に対する=演算子の効果はデータのコピーであり、これは定義型に関しても変わらないということです。