C++ の class を引数として渡すときは、値とアドレス、どちらが良いかという疑問。それぞれトレードオフがあるが、継承した場合の問題を考えると、class はアドレス渡しのほうが良さそう。
C++ は以下のどちらの方法でも結果的にアドレス渡しになる。ただし、(2)は C++ のみの機能。
(1)仮引数は"*"を付けてポインタを指し、実引数は"&"を付けてアドレスを指す場合
(2)仮引数に"&"を付けて参照を指し、実引数は何も付けずに値を渡す場合
両者の違いはコピーコンストラクタがわかりやすい。アドレスのコピーなら(1)のコピーコンストラクタが、値のコピーなら(2)のコピーコンストラクタが呼ばれる。※コピーコンストラクタが未定義であれば値の複製が自動実行される。
const 付で渡した class は const メンバ関数しか呼び出すことができない。参照渡しは const で変更されないことを保証、ポインタ渡しは非 const で変更される可能性がある、とルールを決めたほうがいいかもしれない。
また、初期化と代入は別ものであるということにも注意をしたい。実体変数の場合は、代入ではコピーコンストラクタは作用しない。C++のクラスで記述と実行される処理は以下のようになる。変数宣言がコンストラクタ実行、代入がバイナリコピーというイメージが浮かび上がる。
class instance; → デフォルトコンストラクタ
class instance(other); → コピーコンストラクタ
※class instance = other; → コピーコンストラクタ
instance = other; → 代入演算子
なお、ポインタ変数の場合は、new で明示的にコンストラクタ実行またはコピーコンストラクタ実行を行い、delete で明示的にデストラクタ実行を行う。
関数の引数値や戻り値ではコピーコンストラクタが自動実行され、vector等のコンテナでも自動実行される。std::vector用にデフォルトコンストラクタとコピーコンストラクタ、配列用にデフォルトコンストラクタ、を定義しておく必要がある。コピーコンストラクタの定義では値の複製が自動実行されないように参照型変数を引数値とする。(値型変数では値の複製が自動実行されてしまう。)
引数が1つのコンストラクタは変換コンストラクタという機能に注意が必要で、explicitキーワードを付けで無効化すると良いが、コピーコンストラクタにはexplicitキーワードを付けてはいけない。
代入でも値の複製を自動実行させずにコピーコンストラクタを作用させるには、代入演算子のオーバーロードを定義する。代入演算子の定義では参照型変数を引数値と戻り値(戻り値は自己参照)とする。なお、コピーコンストラクタ、代入演算子のオーバーロード、いずれも、継承クラスでは基底クラスのものを明示的に呼び出す必要がある。
C++11 以降はコピーのほかにムーブというものも登場しておりコピーの例外安全にも関係しているようだ。
野良C++erの雑記帳:「 Copy して Swap 」 対 「 Copy して Move 代入」
ムーブ代入演算子をオーバロードした際に基底クラスのムーブ代入演算子を呼び出す書き方の例
class::operator=(std::move(other))
Effective C++ 総集編:コピーするときは、オブジェクトの全体をコピーしよう
<参考>
吉田誠一のホームページ:値渡しか、参照渡しか
プログラマーズ雑記帳:C++ のコピーコンストラクターと代入演算子
WisdomSoft(旧):C++入門 17. コピーコンストラクタ
satosystemsの日記:std::vector に入れる独自クラスにはデフォルトコンストラクタが必要
Programming Place Plus C++編【言語解説】:第19章 演算子オーバーロード
nyanp::blog:引数を参照とポインタのどちらで渡すか
ロベールのC++教室:第2部 - 第23章 同姓同名2
ロベールのC++教室:第2部 - 第23章 同姓同名3
C++ は以下のどちらの方法でも結果的にアドレス渡しになる。ただし、(2)は C++ のみの機能。
(1)仮引数は"*"を付けてポインタを指し、実引数は"&"を付けてアドレスを指す場合
(2)仮引数に"&"を付けて参照を指し、実引数は何も付けずに値を渡す場合
両者の違いはコピーコンストラクタがわかりやすい。アドレスのコピーなら(1)のコピーコンストラクタが、値のコピーなら(2)のコピーコンストラクタが呼ばれる。※コピーコンストラクタが未定義であれば値の複製が自動実行される。
const 付で渡した class は const メンバ関数しか呼び出すことができない。参照渡しは const で変更されないことを保証、ポインタ渡しは非 const で変更される可能性がある、とルールを決めたほうがいいかもしれない。
また、初期化と代入は別ものであるということにも注意をしたい。実体変数の場合は、代入ではコピーコンストラクタは作用しない。C++のクラスで記述と実行される処理は以下のようになる。変数宣言がコンストラクタ実行、代入がバイナリコピーというイメージが浮かび上がる。
class instance; → デフォルトコンストラクタ
class instance(other); → コピーコンストラクタ
※class instance = other; → コピーコンストラクタ
instance = other; → 代入演算子
なお、ポインタ変数の場合は、new で明示的にコンストラクタ実行またはコピーコンストラクタ実行を行い、delete で明示的にデストラクタ実行を行う。
関数の引数値や戻り値ではコピーコンストラクタが自動実行され、vector等のコンテナでも自動実行される。std::vector用にデフォルトコンストラクタとコピーコンストラクタ、配列用にデフォルトコンストラクタ、を定義しておく必要がある。コピーコンストラクタの定義では値の複製が自動実行されないように参照型変数を引数値とする。(値型変数では値の複製が自動実行されてしまう。)
引数が1つのコンストラクタは変換コンストラクタという機能に注意が必要で、explicitキーワードを付けで無効化すると良いが、コピーコンストラクタにはexplicitキーワードを付けてはいけない。
代入でも値の複製を自動実行させずにコピーコンストラクタを作用させるには、代入演算子のオーバーロードを定義する。代入演算子の定義では参照型変数を引数値と戻り値(戻り値は自己参照)とする。なお、コピーコンストラクタ、代入演算子のオーバーロード、いずれも、継承クラスでは基底クラスのものを明示的に呼び出す必要がある。
C++11 以降はコピーのほかにムーブというものも登場しておりコピーの例外安全にも関係しているようだ。
野良C++erの雑記帳:「 Copy して Swap 」 対 「 Copy して Move 代入」
ムーブ代入演算子をオーバロードした際に基底クラスのムーブ代入演算子を呼び出す書き方の例
class::operator=(std::move(other))
Effective C++ 総集編:コピーするときは、オブジェクトの全体をコピーしよう
<参考>
吉田誠一のホームページ:値渡しか、参照渡しか
プログラマーズ雑記帳:C++ のコピーコンストラクターと代入演算子
WisdomSoft(旧):C++入門 17. コピーコンストラクタ
satosystemsの日記:std::vector に入れる独自クラスにはデフォルトコンストラクタが必要
Programming Place Plus C++編【言語解説】:第19章 演算子オーバーロード
nyanp::blog:引数を参照とポインタのどちらで渡すか
ロベールのC++教室:第2部 - 第23章 同姓同名2
ロベールのC++教室:第2部 - 第23章 同姓同名3