クラスの operator を定義するとき、戻り値の型はどうすべきか
クラスの operator を定義するとき、戻り値の型はどうすべきかのまとめ。
- 全体まとめ
- 代入演算(「=」)
- 和差積商(「+」「-」「*」「/」)
- 自身に対する和差積商(「+=」「-=」「*=」「/=」)
- 比較演算(「==」「!=」「<」「>」)
- 配列アクセス(「[ ]」)
- 実装例
- 参考
サンプルクラス
以下のクラスに、 operator を定義するとします。
全体まとめ
- 演算の連鎖ができるように、戻り値の型を void ではなく、ユーザー定義型にする。
- 自身を返す関数は、戻り値の型を、非const参照にする。
- ユーザー定義型の一時オブジェクトを返す関数は、戻り値の型を、const実体にする。
- 組み込み型の一時オブジェクトを返す関数は、戻り値の型を、const実体にしても、非const実体にしても違いがない。
- メンバ変数の参照を返す関数は、書き込み用関数は、戻り値の型を非const参照、関数を非constとし、constオブジェクトからは呼び出せないようにする。書き込み用の関数を用意するときには、constオブジェクト用に戻り値の型をconst参照、関数をconstとした読み取り用関数も用意する。
代入演算(「=」)
結論
CPoint3d& CPoint3d::operator = ( const CPoint3d& b );
考察(他の選択肢)
- ①戻り値の型を void にする。
- void CPoint3d::operator = ( const CPoint3d& b );
戻り値の型を void にすると、代入の連鎖ができなくなる。すなわち、CPoint3d point1(1,1,1), point2(2,2,2), point3(3,3,3);
point1 = point2 = point3;
組み込み型ではできるので、ユーザー定義型でもできる方がよりよい。
int i1=1, i2=2, i3=3;
i1 = i2 = i3; - ②戻り値の型に const をつける。
- const CPoint3d& CPoint3d::operator = ( const CPoint3d& b );
戻り値の型に const をつけると、
CPoint3d point1(1,1,1), point2(2,2,2), point3(3,3,3);
(point1 = point2) = point3;
組み込み型ではできるので、ユーザー定義型でもできる方がよりよい。
int i1=1, i2=2, i3=3;
(i1 = i2) = i3;
和差積商(「+」「-」「*」「/」)
結論
const CPoint3d CPoint3d::operator + ( const CPoint3d& b ) const;
const CPoint3d CPoint3d::operator - ( const CPoint3d& b ) const;
const CPoint3d CPoint3d::operator * ( const CPoint3d& b ) const;
const CPoint3d CPoint3d::operator / ( const CPoint3d& b ) const;
考察(他の選択肢)
- ①戻り値の型を void にする。
- void CPoint3d::operator + ( const CPoint3d& b ) const;
関数が仕事をしなくなる。 - ②戻り値の型に const をつけない。
- CPoint3d CPoint3d::operator + ( const CPoint3d& b ) const;
戻り値の型に const をつけないと、
CPoint3d point1(1,1,1), point2(2,2,2), point3(3,3,3);
(point1 + point2) = point3;
組み込み型ではできないので、ユーザー定義型でもできない方がよりよい。
int i1=1, i2=2, i3=3;
(i1 + i2) = i3; - ③戻り値の型を参照にする。
- const CPoint3d& CPoint3d::operator / ( const CPoint3d& b ) const;
関数から返るオブジェクトは、関数内で宣言された一時オブジェクトなので、参照を返してはいけない。
自身に対する和差積商(「+=」「-=」「*=」「/=」)
結論
CPoint3d& CPoint3d::operator += ( const CPoint3d& b );
CPoint3d& CPoint3d::operator -= ( const CPoint3d& b );
CPoint3d& CPoint3d::operator *= ( const CPoint3d& b );
CPoint3d& CPoint3d::operator /= ( const CPoint3d& b );
考察(他の選択肢)
- ①戻り値の型を void にする。
- void CPoint3d::operator += ( const CPoint3d& b );
戻り値の型を void にすると、演算の連鎖ができなくなる。すなわち、CPoint3d point1(1,1,1), point2(2,2,2), point3(3,3,3);
point1 += point2 += point3;
組み込み型ではできるので、ユーザー定義型でもできる方がよりよい。
int i1=1, i2=2, i3=3;
i1 += i2 += i3; - ②戻り値の型に const をつける。
- const CPoint3d& operator += ( const CPoint3d& b );
戻り値の型に const をつけると、
CPoint3d point1(1,1,1), point2(2,2,2), point3(3,3,3);
(point1 += point2) = point3;
組み込み型ではできるので、ユーザー定義型でもできる方がよりよい。
int i1=1, i2=2, i3=3;
(i1 += i2) = i3;
比較演算(「==」「!=」「<」「>」)
結論
bool CPoint3d::operator == ( const CPoint3d& b ) const;
bool CPoint3d::operator != ( const CPoint3d& b ) const;
bool CPoint3d::operator < ( const CPoint3d& b ) const;
bool CPoint3d::operator > ( const CPoint3d& b ) const;
考察(他の選択肢)
- ①戻り値の型を void にする。
- void CPoint3d::operator == ( const CPoint3d& b ) const;
関数が仕事をしなくなる。 - ②戻り値の型に const をつける。
- const bool CPoint3d::operator == ( const CPoint3d& b ) const;
constをつけても、つけなくても違いがない。
配列アクセス(「[ ]」)
結論
const double& CPoint3d::operator [ ] ( const long i ) const; // 読み取り用
double& CPoint3d::operator [ ] ( const long i ); // 書き込み用
考察
書き込み用関数は、戻り値の型を非const参照、関数を非constとし、constオブジェクトからは呼び出せないようにする。
書き込み用の関数だけ用意し、読み取り用の関数を用意しないと、非constオブジェクトは値を読み取れるが、constオブジェクトは値を読み取りることができないという問題が生じるので、書き込み用の関数を用意するときには、constオブジェクト用に戻り値の型をconst参照、関数をconstとした読み取り用関数も用意する。
実装例
参考
Effective C++ 改訂2版:15項 operator=を書くときは、*thisへのリファレンスを返そう
Effective C++ 改訂2版:21項 使えるときは、必ずconstを使おう