コピーコンストラクタ&代入演算子&ムーブコンストラクタ&ムーブ代入演算子
C++って言語仕様が多くて大変なので
勉強&復習しながら メモ残します
ってことで、結構難しい コンストラクタちゃんです。
本当はコンストラクタだけで 5,6個はTipsあるのですが
とりま コンストラクタの種類
class Cがあった場合
C(); // デフォルトコンストラクタ
C( const C& rhs); // コピーコンストラクタ
C& operator=( const C& rhs); // 代入演算子
上記3つはコンパイラによって自動生成されます。が、メンバをそのままmemcpyするので
たとえばポインタを内部で持っている場合は、コピーしたClassも同じポインタを参照するので
コピー先がポインタ削除したらコピー元は落ちちゃいます。 ってので デフォルトコンストラクタには注意
C( C&& rhs); // ムーブコンストラクタ
C& operator=( C&& rhs); // ムーブ代入演算子
C++11で新しく出てきたやつですね。
そんなわけで、呼んでみるテスト
#include <iostream>
#include<vector>
#include <string>
using namespace std;
template<class T>
void print( const char* c, T& t )
{
cout << c << t.s << endl;
};
class C
{
private:
vector<int> n;
public:
string s;
C()
: n(1)
, s("DefaultConstuctor")
{
;
};
C( const C& rhs)
: s("CopyConstuctor")
{
n = rhs.n;
};
C( C&& rhs)
: s("MoveConstuctor")
{
n = move(rhs.n);
};
C& operator=( const C& rhs)
{
n = rhs.n;
s="CopyAssignmentOperator";
return *this;
};
C& operator=( C&& rhs)
{
n = move(rhs.n);
s="MoveAssignmentOperator";
return *this;
};
};
void main()
{
C c;
print( "C c:\t\t", c );
C c2(c);
print( "C c2(c):\t", c2 );
C c3 = c;
print( "C c3 = c:\t", c3 );
C c4;
c4 = c;
print( "c4 = c:\t\t", c4 );
C c5(move(c));
print( "C c5(move(c)):\t", c5 );
C c6 = move(c2);
print( "C c6=move(c2):\t", c6 );
C c7;
c6 = move(c3);
print( "c7 = move(c3):\t", c7 );
}
実行結果
C c: DefaultConstuctor
C c2(c): CopyConstuctor
C c3 = c: CopyConstuctor
c4 = c: CopyAssignmentOperator
C c5(move(c)): MoveConstuctor
C c6=move(c2): MoveConstuctor
c7 = move(c3): MoveAssignmentOperator
色々注目はあるんだけど
まず、コンストラクタ、代入演算子では privateメンバを参照できます! そういうものです。
そして注目は
C c3 = c;
パッと見で直訳すると、 c3をデフォルトコンストラクタで生成し 代入演算子で cからコピー
ですが、 コピーコンストラクタにより cから生成する方が早いですよね
ってことでコンパイラが気を利かせて = 使ってるけどコピーコンストラクタで実装してくれます。
ムーブは std::move 使ってください。で、これは 所有権を移動するのです。
たとえば上記ソースでは vector
これがたとえば要素数が100万とかあった場合、 コピーにコストがかかるので
ムーブ(ポインターをすげ替える)のが std::moveです。
そして ムーブ元は ポインタがNULL になります
ので、
C d(move(c)); // cのデータの所有権をdへ譲渡した
この後 c.n をアクセスするとヌルポエラーになる
ってことで std::moveや そうですね explicitの話その他は別の時に。