no-image

【C++】変数の生成と代入のタイミング ~コンストラクタ・コピーコンストラクタ・代入演算子~

はじめに

変数の生成と代入にあたって、呼ばれる関数(コンストラクタ・コピーコンストラクタ・代入演算子)とタイミングの違いについて取り上げていきます。

違いを理解するにあたって、コンストラクタとコピーコンストラクタと代入演算子は切っても切り離せない関係です。

自作クラスの生成の話を始める前に、まず、身近に使っているint型を例に挙げると…

変数を使う方法 ~2通り~

代入

変数を宣言(生成)したに値を代入している。これが代入

初期化

のように変数の宣言(生成)と同時に値を代入する。これが初期化

  注)「代入する」と言ったが、宣言と同時の場合、初期化という意味に変わることに注意。

また、実は、以下のような書き方もC++ではでき、先程と同じ意味。

C言語ではできなかったが…

代入タイミングが宣言と同時であることには変わりはありません。

代入のタイミングが違う!

このタイミングの違いによって、その都度内部で呼ばれる関数が違うというのが、今から話す内容です。

 

呼ばれる関数の違い

呼ばれる関数である、コンストラクタ・コピーコンストラクタ・代入演算子のオーバーロードを以下の通り定義した。

  注)普段これらは、自分から定義しなければ自動で定義される。

num 変数は代入演算子オーバーロード関数内の計算用。

これで、タイミングを調べていく。

コンストラクタ呼ばれるタイミング

初期化したとき ~2通り~ 

ここでは、コンストラクタの引数は int型なので、int型で初期化したとき。

デフォルトコンストラクタであれば、引数なしのとき呼ばれます。

次項目で説明しますが、ここでの引数は自分の型と同じではないことが重要です。

普通に初期化したとき

new 演算子で新たに生成したとき

new 演算子を使った右辺の段階で生成されています。

  注)左辺への代入は、ポインタの指し示す矢印を決めただけで、生成には関与しないことに注意。

コピーコンストラクタが呼ばれるタイミング 

コピーコンストラクタとは、一言でいうと

コンストラクタの引数を自分と同じ型にしたバージョン

つまり、私たちが普段作る、いろんな引数をとるコンストラクタのバリエーションの一種にすぎないのです。

仮引数のconstや&は、参照渡しによる速度向上が目的なので、本質ではないです。ただ、付けないとコピーコンストラクタだと認めてもらえないので、付けましょう。

自分の型と 同じクラス (または 派生クラス) の引数でもって初期化したとき ~2通り~

ここで、自分は Obj型なので、Obj型で初期化したときです。

普通に初期化したとき

「はじめに」の項目で説明した通り、2行目は

のようにも、記述できます。

関数の仮引数に渡すとき

関数内の仮引数はスコープに従って、その中、限定の変数が作られるのでした。

つまり、

Obj f_obj = obj;

という初期化が仮引数に渡す際 func関数内で行われているのです。

ちゃんと、同じ型で初期化されていますね。

代入演算子のオーバーロードが呼ばれるタイミング

代入したとき 

注)「はじめに」の項目で説明した通り、代入と初期化は違う!

普通に関数名で呼び出したとき

実は、3行目は

のように置き換えることができます。

=演算子の代入は、この関数名を呼び出す手間を省いてくれていたんですね。

まとめ

コピーコンストラクタはコンストラクタの一種

引数の違い
 コピーコンストラクタ・・・自分と同じ型(1つだけ引数とる)
    コンストラクタ・・・コピーコンストラクタの引数条件以外

コンストラクタは初期化時に呼ばれる

   初期化 宣言と代入が同じタイミング

代入時の代入(=)演算子はoperator関数の呼び出しを省略していた

 

参考

コピーコンストラクタって? – Qiita:https://qiita.com/youthini/items/2314ad7cf498e5f199d7

書籍「Effective C++ 第3版」