# コピーの禁止

型によっては、コピーという概念が存在しないものがある。

例えばコピー不可能なシステムのリソースを扱うクラスだ。

具体的にはファイル、スレッド、プロセス、ネットワークソケットといったリソースだ。このようなリソースを管理するクラスを作ったとして、いったいコピーをどうすればいいのだろうか。

コピーできないクラスはdeleted定義を使ってコピーコンストラクターとコピー代入演算子を消すことができる。

deleted定義は関数の本体{...}の代わりに= deleteを書く。deleted定義されている関数を使うとエラーとなる。

struct X
{
    // コピーコンストラクター
    X( const X & ) = delete ;
    // コピー代入演算子
    X & operator = ( const X & ) = delete ;

    // デフォルトコンストラクター
    X() { }
    // ムーブコンストラクター
    X ( X && ) { }
    // ムーブ代入演算子
    X & operator = ( X && ) { }
} ;

このようなクラスXは、コピーできない。

int main()
{
    // デフォルト構築できる
    X a ;
    // エラー、コピーできない
    X b = a ;
    b = a ;
    // OK、ムーブはできる。
    X c = std::move(a) ;
}

クラスXはコピーコンスラクターとコピー代入演算子がdeleted定義されているために、コピーをすることができない。

コピーやムーブが禁止されている型をデータメンバーに持つクラスは、デフォルトのコピーやムーブができなくなる。

// コピーできない型
struct Uncopyable
{
    Uncopyable(){}
    Uncopyable( const Uncopyable & ) = delete ;
    Uncopyable & operator = ( const Uncopyable & ) = delete ; 
} ;

// デフォルトのコピーができない
struct X
{
    Uncopyable member ;
} ;

deleted定義を使えばムーブも禁止できる。ただし、ムーブを禁止するというのは現実的にはあまり実用性がない。というのも、コピーはムーブでもあるので、コピーを提供している型はムーブとしてコピーを行えばムーブも提供できることになる。

# 5原則

C++には「5原則」という作法がある。

5原則とは、

  1. コピーコンストラクター
  2. コピー代入演算子
  3. ムーブコンストラクター
  4. ムーブ代入演算子
  5. デストラクター

このうちの1つを独自に定義したならば、残りの4つも定義すべきである。

というものだ。

なぜか。コピーやムーブを独自に定義するということは、デフォルトのコピーやムーブでは足りない何らかの処理をしたいはずだ。その処理には、たいていの場合何らかの破棄の処理が必要で、するとデストラクターも定義しなければならない。

同様に、デストラクターで何らかの独自の処理をするということは、コピーやムーブでも何らかの処理をしたいはずだ。