クラス内のvoid関数を連続で呼び出したいとき、複数行にまたがって何度も呼び出している場合があります。
メソッドチェーンを使用すればそのようなコードを簡潔な記述にすることができます。
今回はスマートポインタを使用してメソッド
前提知識
- クラスについてがある程度理解できている
- スマートポインタがある程度理解できている
メソッドチェーンとは
メソッドチェーンとは、戻り値として自身を返すことで関数を連続して呼び出す仕組みのことです。
ゲーム開発などでは、コンポーネントの取得関数などに使用されます。
とりあえず自身を返してみる。
本記事では整数値のみを扱える電卓のようなクラスを作り、それにメソッドチェーンを実装してみたいと思います。
#include <iostream>
#include <memory>
//! @class IntegerCalculator
//! @brief 整数の演算を行うクラス
class IntegerCalculator {
public:
//コンストラクタ
// @param init_val [in] 演算値の初期値
IntegerCalculator(int init_val);
//加算
//@ param add_val [in] 加算する整数値
std::shared_ptr<IntegerCalculator> Add(int add_val);
//減算
//@ param add_val [in] 減算する整数値
std::shared_ptr<IntegerCalculator> Sub(int sub_val);
//乗算
//@ param add_val [in] 乗算する整数値
std::shared_ptr<IntegerCalculator> Mul(int mul_val);
//除算
//@ param add_val [in] 除算する整数値
std::shared_ptr<IntegerCalculator> Div(int div_val);
//結果を表示
std::shared_ptr<IntegerCalculator> ShowResult();
private:
int value_;//演算中の値
};
//---------------------------------------------------------------------------------
//! @brief コンストラクタ
//---------------------------------------------------------------------------------
IntegerCalculator::IntegerCalculator(int init_val) {
value_ = init_val;
}
//---------------------------------------------------------------------------------
//! @brief 加算
//---------------------------------------------------------------------------------
std::shared_ptr<IntegerCalculator> IntegerCalculator::Add(int add_val) {
value_ += add_val;
return std::shared_ptr<IntegerCalculator>(this);
}
//---------------------------------------------------------------------------------
//! @brief 減算
//---------------------------------------------------------------------------------
std::shared_ptr<IntegerCalculator> IntegerCalculator::Sub(int sub_val) {
value_ -= sub_val;
return std::shared_ptr<IntegerCalculator>(this);
}
//---------------------------------------------------------------------------------
//! @brief 乗算
//---------------------------------------------------------------------------------
std::shared_ptr<IntegerCalculator> IntegerCalculator::Mul(int mul_val) {
value_ *= mul_val;
return std::shared_ptr<IntegerCalculator>(this);
}
//---------------------------------------------------------------------------------
//! @brief 除算
//---------------------------------------------------------------------------------
std::shared_ptr<IntegerCalculator> IntegerCalculator::Div(int div_val) {
value_ /= div_val;
return std::shared_ptr<IntegerCalculator>(this);
}
//---------------------------------------------------------------------------------
//! @brief 結果の表示
//---------------------------------------------------------------------------------
std::shared_ptr<IntegerCalculator> IntegerCalculator::ShowResult() {
printf_s("演算結果 : %d\n", value_);
return std::shared_ptr<IntegerCalculator>(this);
}
int main() {
//電卓クラスを宣言して初期化
std::shared_ptr<IntegerCalculator> calculator = std::make_shared<IntegerCalculator>(0);
//メソッドチェーンで適当に計算してみる
calculator->Add(5)->ShowResult()->Div(5)->ShowResult()->Mul(8)->ShowResult()->Sub(7)->ShowResult();
system("pause");
return 0;
}
演算結果 : 5
演算結果 : 1
演算結果 : 8
演算結果 : 1
ブレークポイント命令 (__debugbreak() ステートメントまたは類似の呼び出し) が method.exe で実行されました。
安直に自身を返すとクラッシュしてしまいました。
問題点はstd::shared_ptr<IntegerCalculator>(this)
の部分です。
this
を渡してshared_ptr<T>(this)
とすると、参照カウントが増えないのです。
そのため、ポインタの二重開放によってクラッシュしてしまいます。
どうすればよかったのか
shared_from_this()
という関数を使用することによって、 安全にthis
を指すshared_ptr
オブジェクトを作ることができます。
shared_from_this()
は<memory>
ヘッダ内のstd:: enable_shared_from_this
クラスを継承することで使用することができます。
#include <iostream>
#include <memory>
//! @class IntegerCalculator
//! @brief 整数の演算を行うクラス
//! @details std::enable_shared_from_this<IntegerCalculator>を継承して、shared_from_this()を使用できるようにしています。
class IntegerCalculator : public std::enable_shared_from_this<IntegerCalculator> {
public:
//コンストラクタ
// @param init_val [in] 演算値の初期値
IntegerCalculator(int init_val);
//加算
//@ param add_val [in] 加算する整数値
std::shared_ptr<IntegerCalculator> Add(int add_val);
//減算
//@ param add_val [in] 減算する整数値
std::shared_ptr<IntegerCalculator> Sub(int sub_val);
//乗算
//@ param add_val [in] 乗算する整数値
std::shared_ptr<IntegerCalculator> Mul(int mul_val);
//除算
//@ param add_val [in] 除算する整数値
std::shared_ptr<IntegerCalculator> Div(int div_val);
//結果を表示
std::shared_ptr<IntegerCalculator> ShowResult();
private:
int value_;//演算中の値
};
//---------------------------------------------------------------------------------
//! @brief コンストラクタ
//---------------------------------------------------------------------------------
IntegerCalculator::IntegerCalculator(int init_val) {
value_ = init_val;
}
//---------------------------------------------------------------------------------
//! @brief 加算
//---------------------------------------------------------------------------------
std::shared_ptr<IntegerCalculator> IntegerCalculator::Add(int add_val) {
value_ += add_val;
return shared_from_this();
}
//---------------------------------------------------------------------------------
//! @brief 減算
//---------------------------------------------------------------------------------
std::shared_ptr<IntegerCalculator> IntegerCalculator::Sub(int sub_val) {
value_ -= sub_val;
return shared_from_this();
}
//---------------------------------------------------------------------------------
//! @brief 乗算
//---------------------------------------------------------------------------------
std::shared_ptr<IntegerCalculator> IntegerCalculator::Mul(int mul_val) {
value_ *= mul_val;
return shared_from_this();
}
//---------------------------------------------------------------------------------
//! @brief 除算
//---------------------------------------------------------------------------------
std::shared_ptr<IntegerCalculator> IntegerCalculator::Div(int div_val) {
value_ /= div_val;
return shared_from_this();
}
//---------------------------------------------------------------------------------
//! @brief 結果の表示
//---------------------------------------------------------------------------------
std::shared_ptr<IntegerCalculator> IntegerCalculator::ShowResult() {
printf_s("演算結果 : %d\n", value_);
return shared_from_this();
}
int main() {
//電卓クラスを宣言して初期化
std::shared_ptr<IntegerCalculator> calculator = std::make_shared<IntegerCalculator>(0);
//メソッドチェーンで適当に計算してみる
calculator->Add(5)->ShowResult()->Div(5)->ShowResult()->Mul(8)->ShowResult()->Sub(7)->ShowResult();
system("pause");
return 0;
}
演算結果 : 5
演算結果 : 1
演算結果 : 8
演算結果 : 1
続行するには何かキーを押してください . . .
クラッシュせずにメソッドチェーンの実装を行うことができました。
総括
- メソッドチェーンを使用することで冗長なコードを避け、処理を簡潔に記述することが可能となる。
- 自身のポインタを戻り値にすることでメソッドチェーンを実装することができる。
-
std::shared_ptr<IntegerCalculator>(this)
とすると参照カウントが増えず、二重開放でクラッシュしてしまう。 -
std::enable_shared_from_this
を継承することで、安全に自身のポインタを戻り値にすることができる。