クラスインスタンスをコピーする。
<確認したい内容>
1.クラスインスタンスをコピーする場合、新たに領域を確保してコピーしているのか, 確認する。
2.代入演算子=のオーバーロードで、きちんとコピーできるか。
3.コピーコンストラクタが起動する場合と、代入演算子=が使用される場合、その用途の違いを確かめる。
そもそも、コピーという言葉があいまいで、どう具体的にコピーされているのか、実コードで確認する。
C++のコピーには、2種類の定義があります。
ディープコピーDeep Copy: 実体を、新たな領域を確保してコピー。
シャローコピーShallow Copy: ポインタ同士の、コピー。
コピーコンストラクタ構文
class class_A
{
public:
class_A(const class_A& existedOtherClass);
};
定義は決まっているので、引数に同じクラスを割り当てると起動します。
事前定義識別子 __PRETTY_FUNCTION__
名前空間名、クラス名、戻り値やパラメータといった情報も含む関数の文字列。
デバッグなどで、実行された関数名を表示したいときに使用します。
クラスインスタンスのコピーを行うときは、メンバー変数に外部本体をもつポインターがあるか、確認しておきます。
コピーしたときに、ポインタが保持しているアドレスはコピーされますが、2つのクラスインスタンスが、一つの実態しかないオブジェクトを、不意に共有してしまう可能性があります。デストラクタの、2重解放もありえます。
コンパイラの自動生成したコピーコンストラクタを使うときは、注意が必要です。
クラスインスタンスのコピー 動作確認コード
__PRETTY__FUNCTION__を使用して、どのメソッドが、どの順番で、起動したか、
確認できるようにしています。
1.CLクラスインスタンスCL1の生成
2.CLクラスインスタンスCL2をCL1のコピーコンストラクタで生成。
※CL1とCL2のアドレスが違うことを確認。
3.CL2のメンバー変数を変更し、CL1に変化がないことを確認。
4.CLクラスインスタンスCL3を代入演算子=を使用して、コピーコンストラクタで生成。
※このとき、代入演算子=のオーバーロードで生成されていないことを確認。
5.CLクラスインスタンスCL4をCLクラスコンストラクタで生成。
6.代入演算子=をつかってCL4にCL1を代入。コピーされているか確認。
※このとき、代入演算子=のオーバーロードが使用されているか、確認。
7.setupを抜けると各インスタンスのデストラクタが起動。その順番を確認。
#include <M5Core2.h>
/**
* コピーコンストラクタ
* ディープコピー
* 事前定義識別子
*/
class CL
{
public:
//コンストラクタ
CL(String s, int n)
{ Serial.printf("%s\r",__PRETTY_FUNCTION__); name=s; num=n; }
//デストラクタ
~CL(){Serial.printf("%s %x\r ",__PRETTY_FUNCTION__, this);}
//コピーコンストラクタ
CL(const CL& _cl)
{ Serial.printf("%s\r",__PRETTY_FUNCTION__); num=_cl.num; name=_cl.name;}
//代入演算子=オーバーロード
CL& operator=(const CL& _cl)
{ Serial.printf("%s\n",__PRETTY_FUNCTION__); num=_cl.num; name=_cl.name; return *this;};
//メンバ変数の表示
void print(String s){Serial.printf("%s Add:%x name:%s,num:%d\r", s, this, name, num);}
public:
String name;
int num;
};
void setup()
{
M5.begin();
Serial.print("------CL1 Construction-------\r");
CL CL1("CL1",1);
CL1.print("CL1");
Serial.print("------CL2 Copy Construction-------\r");
CL CL2(CL1);
CL1.print("CL1");
CL2.print("CL2");
Serial.print("------Change CL2 values-------\r");
CL2.name="CL2";
CL2.num=2;
CL1.print("CL1");
CL2.print("CL2");
Serial.print("------Initial Copy CL3=CL1 ----------\r");
CL CL3 = CL1;
CL1.print("CL1");
CL2.print("CL2");
CL3.print("CL3");
Serial.print("------CL4 Construction ----------\r");
CL CL4("CL4",4);
CL4.print("CL4");
Serial.print("------CL4 = CL1 ----------\r");
CL4 = CL1;
CL1.print("CL1");
CL4.print("CL4");
Serial.print("------Close destruction ----------\r");
}
void loop()
{
delay(1);
}
今回は、ディープコピーで、実体を2つに増やすことをメインに検証しました。
インスタンスコピーに関して、理解の解像度が上がったと思います。
<参照サイト>
