C++の値オブジェクトと参照オブジェクト
注意: この記事には不確かなことが書かれていることがあります。
C++ に詳しい諸兄におかれまして、もし間違いなどを見つけられましたら、コメント欄にてご意見をお寄せください。
最近、研究室にあったCで書かれたプログラムを、 C++ で書き直しています。
目的は、研究のため、今後の後輩のため、そしてマルチプラットフォーム化のためです。
特に3つ目の目的のため、初めて Qt を使ったプログラムを書いています。
さて、書くために C++ について勉強していて、こんな表記にぶつかりました。
// 値オブジェクト ClassA a1; // 参照オブジェクト ClassA *a2 = new ClassA();
これは一体なんなのでしょうか。
どんなときにどちらを使えば良いのでしょうか。
そして、どう使えば便利なのでしょうか。
C# に慣れ親しんでいた私には違いがよくわからないので、調べてみました。
値オブジェクト
int 型のように、プリミティブな型と同じ表記でインスタンス化したオブジェクトです。
言葉の定義は、C++ クラス設計に関するノートによれば、
次のような特徴をもったオブジェクトを「値オブジェクト」といいます。
C 言語で言う所の struct とほとんど同じような使い方をするクラスが、これに該当するようです。
例えば、「本」を表すこんなクラスがあったとします。
class Book { public: string title; string author; bool isFavorite(); private: bool m_isFavorite; };
これはインスタンスの存在そのものより、そこに格納されている値が重要ですよね。
また、値オブジェクトは immutable にするのが推奨されるとか。
そうですよね。値の状態が知らん間にコロコロ変わってたりされたら迷惑至極です。
参照オブジェクト
定義は、
次のような特徴をもったオブジェクトを「参照オブジェクト」といいます。
ということで、何かしらの機能を提供するようなクラスのインスタンスがこれにあたりそうです。
例えば、 TCP のソケット通信を行う、 Qt の QTcpSocket などがこれにあたるでしょうか。
同じサーバーに接続するクライアントが二つあったとしても、この二つは別物でなければいけません。同じものだと通信が混乱します。
それに、コネクションのコピーを作成することも、通常はあり得ませんよね。
また、このオブジェクトは mutable で良いそうです。
どう使うか
私が気にしているのは、以下の点です。
- オブジェクトの寿命はどうなっているのか
- メソッドの引数に渡す時はどうするのか
- メソッドの戻り値にする時はどうするのか
一つずつ見て行きましょう。
オブジェクトの寿命はどうなっているのか
値オブジェクトについては、 @satoru_takeuchi 先生に教えていただきました。
@kikuchy そうだよ。スタックに置いてるから
— satさん (@satoru_takeuchi) 2013年5月3日
ということで、インスタンスを生成したスコープの中でのみ生きているようです。
スコープの外に持ち出すには、値のコピーが必要ですね。
参照オブジェクトについてはよく言われている通り、 delete 演算子で消すまでオブジェクトが生き残り続けます。
よくメモリリークの原因になるあれですね。
メソッドの引数に渡す時はどうするのか
例を見れば早そうですね。
// 値オブジェクトの渡し方 その1(参照渡し) void methodA(Class &a); // 値オブジェクトの渡し方 その2(値コピー) void methodB(Class a); // 参照オブジェクトの渡し方 void methodC(ClassA *a);
値オブジェクトの渡し方は二つ。
「その1」の方は、このメソッド (methodA) を呼び出しているスコープが生きているときならば引数のオブジェクトを参照できますが、スコープが切れると参照できなくなりそうな気がします。非同期処理をするときには注意が必要そうです。
「その2」の方は、値が丸々コピーされているので、外のスコープが切れていても参照できます。また、メソッド終了時に自動的に破棄されます。便利ですが、コピーにコストがかかるのが難点。
参照オブジェクトの渡し方については、特に何も言う事は無いですね。