3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

クラス内のvoid関数を連続で呼び出したいとき、複数行にまたがって何度も呼び出している場合があります。
メソッドチェーンを使用すればそのようなコードを簡潔な記述にすることができます。
今回はスマートポインタを使用してメソッド

前提知識

  • クラスについてがある程度理解できている
  • スマートポインタがある程度理解できている

メソッドチェーンとは

メソッドチェーンとは、戻り値として自身を返すことで関数を連続して呼び出す仕組みのことです。
ゲーム開発などでは、コンポーネントの取得関数などに使用されます。

とりあえず自身を返してみる。

本記事では整数値のみを扱える電卓のようなクラスを作り、それにメソッドチェーンを実装してみたいと思います。

MethodChaining1.cpp
#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;
}
result
演算結果 : 5
演算結果 : 1
演算結果 : 8
演算結果 : 1
error(delete_scalar.cpp : 34行目)
ブレークポイント命令 (__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クラスを継承することで使用することができます。

MethodChaining2.cpp
#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;
}
result
演算結果 : 5
演算結果 : 1
演算結果 : 8
演算結果 : 1
続行するには何かキーを押してください . . .

クラッシュせずにメソッドチェーンの実装を行うことができました。

総括

  • メソッドチェーンを使用することで冗長なコードを避け、処理を簡潔に記述することが可能となる。
  • 自身のポインタを戻り値にすることでメソッドチェーンを実装することができる。
  • std::shared_ptr<IntegerCalculator>(this)とすると参照カウントが増えず、二重開放でクラッシュしてしまう。
  • std::enable_shared_from_thisを継承することで、安全に自身のポインタを戻り値にすることができる。
3
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?