Help us understand the problem. What is going on with this article?

コンストラクタが暗黙に宣言されるとき、されないとき

More than 3 years have passed since last update.

こんにちは。
最近勉強し始めたc#でWPFをやってみましたが、もう見たくもありません。

さて、今日はc++を使っていても意外になかなか覚えない、コンストラクタの暗黙の宣言 / 非宣言をまとめておきたいと思います。
関連して、よく問題になる暗黙のコピー、ムーブの問題についても書き留めておこうと考えています。

コンパイラが用意してくれる特殊関数は以下になります。(c++14時点)

デフォルトコンストラクタ
コピーコンストラクタ
コピー代入演算子
ムーブコンストラクタ
ムーブ代入演算子
デストラクタ

何事もなければこれらが全て暗黙的に宣言・定義されます。( class something {}; )
平和ですね。みんながみんなこうであればよいのですが、そうはいかないのです。

コンストラクタが暗黙に宣言されるとき、されないとき

ざっくり以下に表にまとめました。
なお、constructorをctor, operatorをop, declarationをdeclと略しています。
[deprecated]は宣言されるものの非推奨の意(後述)です。
見方としては、例えば何かしらのコンストラクタを自前で書いた場合、横へ見ていって、デフォルトコンストラクタは宣言されない、コピームーブデストラクタはdefaultだな、といった感じです。

default ctor copy ctor / op move ctor / op destructor
any ctors no decl default default default
copy ctor / op no decl - no decl default
move ctor / op no decl [deprecated] - default
destrctor default [deprecated] [deprecated] -

パクリ元参考:
特殊メンバ関数とコンパイラによる暗黙宣言
本の虫: C++0xにすごい変更が来た

なお、メンバ変数や基底クラスでdeleteされている関数は暗黙的にdeleteされます。
これは宣言されない(no decl)ではなく、削除(delete)なので、仮に明示的なデフォルト宣言(=default)をしたとしてもdeleted functionという扱いになります。

暗黙的コピーとムーブが定義されることの問題

上の図では、3箇所に[deprecated]が入っています。
定義されるものの非推奨と言ったのは、これらは本来暗黙的に定義されてはならないと言ってもいいものだからです。

例えば、デストラクタを自前で用意したときのcopy / moveはdeprecatedになっていますね。
デストラクタは何かリソースを解放するためのものと言っても過言ではありません。何かリソースを解放しなければならないクラスというのは、そのリソースについて所有権を持っており、責任を負っているということです。

class sth0
{
  int* p;
public:
  sth0() p( new int() ) {}
  ~sth0() { delete p; } 
};

リソースはなんでもいいですが、例えばこのようなクラスを書けば、ひと目で多重解放の危険があることが分かるでしょう。デストラクタを定義する場合のほとんどがこのようにリソースを管理する場合のはずです。

ゆえに、本当にデフォルト処理のコピーやムーブで構わないのであれば、それを明示してあげるべきです。

class sth1
{
public:
  // sth1() = default; // if you need
  sth1( const sth1& ) = default;
  sth1& operator ( const sth1& ) = default;
  sth1( sth1&& ) = default;
  sth1& operator ( sth1&& ) = default;
  ~sth1() { std::cout << "output log" << std::endl; }
};

このクラスでは、デストラクタの処理はログを記録するだけです。
なのでコピーやムーブはデフォルトのものを使って欲しいと明示してあげます。
なお、コピーやムーブコンストラクタを宣言することでデフォルトコンストラクタが宣言されなくなります。
もし必要であればデフォルトコンストラクタもdefaultで明示してあげなくてはなりません。

ここまでくればムーブを明示的に宣言したときにコピーが暗黙宣言されてしまうことがdeprecatedであることの理由は分かるかと思います。

何にせよ、全ての特殊関数をいちいち明示的に宣言/削除してあげるのが確実ですね。
少なくとも、人に読ませるようなコードではそうしたほうがいいかもしれません。

class something
{
  // ctor
  something() noexcept = default;
  // copy
  something( const something& ) = default;
  something& operator = ( const something& ) = default;
  // move
  something( something&& ) noexcept = default;
  something& operator = ( something&& ) noexcept = default;
  // dtor
  ~something() noexcept = default;
};
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away