ゆき社長

シーゲンガーのお勉強 ゲームプログラマ、ゲーマー、色々!

コピーコンストラクタ&代入演算子&ムーブコンストラクタ&ムーブ代入演算子

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の話その他は別の時に。