はじめに
Effective C++ 第3版の10項 48ページから勉強していきます。
今回は、「代入演算子は*thisへの参照を戻すようにしよう」についです。
Effective C++ 第3版 - 10項 代入演算子は*thisへの参照を戻すようにしよう -
前置き
今回は、「自分で作成したクラスにコピー代入演算子を実装する場合のルール」について学んでいきます。
今回の勉強
コピー代入演算子
コピー代入演算子は、以下のようにつなげて使用することができます。
int x, y, z;
x = y = z = 15; // 代入をつなげる
コピー代入演算子は、右結合である。
→右から左へ結合していく
そのため、上のような連続代入は、以下のように解釈されます。
まず、15がzに代入され、
次に、その結果(更新されたz)がyに代入され、
最後に、その結果(更新されたy)がxに代入される。
x = (y = (z = 15));
これは、コピー代入演算子が「左辺への参照」を戻すことで実現されています。
自分で作成したクラスにコピー代入演算子を実装する場合は、以下のようになります。
class Widget {
public:
/* コンストラクタ */
Widget(int x, int y) : x_(x), y_(y) {}
/* コピー代入演算子 */
Widget& operator=(const Widget& rhs) { // 戻り値の方は、自身のクラスの参照型
x_ = rhs.x_;
y_ = rhs.y_;
return *this; // 左辺への参照を返す(自分自身を返す)
}
int getX() { return x_; }
int getY() { return y_; }
private:
int x_;
int y_;
};
また、通常の代入(=)に限らず、全ての代入演算子(+=, -= など)に上のように書く必要があります。
また、仮引数の型が違っても、同様に *this を返す必要があります。
class Widget {
public:
...
Widget& operator+=(const Widget& rhs) { // +=, -=, *= でも*thisを返す。
std::cout << __func__ << std::endl;
x_ += rhs.x_;
y_ += rhs.y_;
return *this;
}
/* 引数がint型 */
Widget& operator*=(int rhs) { // 引数の型が違う場合でも *thisを返す。
std::cout << __func__ << std::endl;
x_ *= rhs;
y_ *= rhs;
return *this;
}
private:
...
};
これらのように、「コピー代入演算子は、*thisへの参照を戻す」というのは、
すべての組み込み型とすべての標準ライブラリの型(string, vector, など)が従っているそうです。
サンプルコード
以下に、勉強で使用したコードを示します。
# include <iostream>
class Widget {
public:
Widget(int x = 0, int y = 0) : x_(x), y_(y) {}
Widget& operator=(const Widget& rhs) {
std::cout << __func__ << std::endl;
x_ = rhs.x_;
y_ = rhs.y_;
return *this;
}
Widget& operator+=(const Widget& rhs) {
std::cout << __func__ << std::endl;
x_ += rhs.x_;
y_ += rhs.y_;
return *this;
}
Widget& operator*=(int rhs) {
std::cout << __func__ << std::endl;
x_ *= rhs;
y_ *= rhs;
return *this;
}
int getX() { return x_; }
int getY() { return y_; }
private:
int x_;
int y_;
};
int main() {
std::cout << "10_first.cpp" << std::endl;
int x, y, z;
x = y = z = 15;
Widget b;
b = Widget(1, 1);
std::cout << "\noperator= Widget(1, 1)" << std::endl;
std::cout << "b.x : " << b.getX() << std::endl;
std::cout << "b.y : " << b.getY() << std::endl;
b += Widget(2, 2);
std::cout << "\noperator+= Widget(2, 2)" << std::endl;
std::cout << "b.x : " << b.getX() << std::endl;
std::cout << "b.y : " << b.getY() << std::endl;
b *= 4;
std::cout << "\noperator*= 4" << std::endl;
std::cout << "b.x : " << b.getX() << std::endl;
std::cout << "b.y : " << b.getY() << std::endl;
}
実行結果
10_qiita.cpp
operator= Widget(1, 1)
b.x : 1
b.y : 1
operator+= Widget(2, 2)
b.x : 3
b.y : 3
operator*= 4
b.x : 12
b.y : 12
まとめ
今回は、自分で作成したクラスにコピー代入演算子を実装する場合のルールについて学びました。
覚えておくこと
- 代入演算子は*thisへの参照を戻すようにしよう。
参考文献
[1] https://www.amazon.co.jp/gp/product/4621066099/ref=dbs_a_def_rwt_hsch_vapi_taft_p1_i0