かつて情報をネットで探したときに、断片的な情報はあるものの、分かりやすいサンプルが見付からなかったのでメモ。
Compositeパターンをshared_ptrで実装するのは、単にポインタをshared_ptrに置き換えるだけだと、循環参照が発生する。
ポイントは2つ。
1.親(Parent)のポインタはshared_ptrでなくweak_ptrに置き換える
2.shared_from_thisを使う
以下のコードはc++11以降のstl版だが、c++11以前でもboostで同様の実装が可能。必要なヘッダをincludeすれば”std::”を”boost::”に置き換えるだけでOK。
(説明を短くするために全てheaderに入れている)
まずComponentクラス。
メンバ”parent_”をweak_ptrで保持する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
#include <memory> #include <iostream> // 動作確認用: //--------------------------------------------------------------------------- class Component { //-------------------------------- // Member and Accessor: //________________________________ private: std::weak_ptr<Component> parent_; public: // ---- get ---- std::shared_ptr<Component> getParent()const{ return parent_.lock(); } bool hasParent()const{ return parent_.lock().get() != nullptr; } // ---- set ---- void setParent( const std::shared_ptr<Component>& compo ){ parent_ = compo; } //-------------------------------- // Constructor: //________________________________ public: Component() : parent_() {} //-------------------------------- // Destructor: //________________________________ public: virtual ~Component(){} //-------------------------------- // Pure Virtual Method: //________________________________ public: virtual void Add( const std::shared_ptr<Component>& compo ) = 0; virtual void Remove( const std::shared_ptr<Component>& compo ) = 0; // virtual void Execute( int depth ) = 0; virtual void doExecute() = 0; }; |
(Executeは階層構造を出力するだけのテスト用Method)
つづいてCompositeクラス。
継承元に”std::enable_shared_from_this”を指定するのがポイント。
また”Add”で親をセットする際、”shared_from_this()”を使う。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
#include <vector> #include <algorithm> //--------------------------------------------------------------------------- #include "Component.h" //--------------------------------------------------------------------------- class Composite : public Component,public std::enable_shared_from_this<Composite> { //-------------------------------- // Member and Accessor: //________________________________ private: std::vector<std::shared_ptr<Component>> childs_; //-------------------------------- // Constructor: //________________________________ public: Composite() : Component(), childs_() {} //-------------------------------- // Override Method: //________________________________ public: virtual void Add( const std::shared_ptr<Component>& compo ) { if( std::find( childs_.begin(), childs_.end(), compo ) == childs_.end() ) { childs_.push_back( compo ); compo->setParent( shared_from_this() ); } } virtual void Remove( const std::shared_ptr<Component>& compo ) { childs_.erase( remove( childs_.begin(), childs_.end(), compo ), childs_.end() ); } // virtual void Execute( int depth ) { for( int i = 0; i < depth; ++i ) std::cout << " "; doExecute(); for( size_t i = 0; i < childs_.size(); ++i ) { childs_[i]->Execute( depth + 1 ); } } }; |
CompositeのConcreteクラスを2つ作ってみる。ひとつはstd::string, もう一つはintをメンバに持つことにする。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
class ConcreteCompositeA : public Composite { //-------------------------------- // Member and Accessor: //________________________________ private: int val_; //-------------------------------- // Constructor: //________________________________ public: explicit ConcreteCompositeA( int val ) : Composite(), val_(val) {} //-------------------------------- // Destructor: //________________________________ public: ~ConcreteCompositeA(){} //-------------------------------- // Override Method: //________________________________ public: virtual void doExecute() { std::cout << "A: val == " << val_ << std::endl; } }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
#include <string> //--------------------------------------------------------------------------- #include "Composite.h" //--------------------------------------------------------------------------- class ConcreteCompositeB : public Composite { //-------------------------------- // Member and Accessor: //________________________________ private: std::string str_; public: // ---- set ---- // ---- get ---- //-------------------------------- // Constructor: //________________________________ public: explicit ConcreteCompositeB( const std::string& str ) : Composite(), str_(str) {} //-------------------------------- // Destructor: //________________________________ public: ~ConcreteCompositeB(){} //-------------------------------- // Override Method: //________________________________ public: virtual void doExecute() { std::cout << "B: str == " << str_ << std::endl; } }; |
実行してみる。適当に親子関係を作り、出力。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#include <stdio.h> #include <conio.h> #include "ConcreteCompositeA.h" #include "ConcreteCompositeB.h" int _tmain(int argc, _TCHAR* argv[]) { std::shared_ptr<Component> c1 = std::make_shared<ConcreteCompositeA>( 3 ); std::shared_ptr<Component> c2 = std::make_shared<ConcreteCompositeB>( "child" ); std::shared_ptr<Component> c3 = std::make_shared<ConcreteCompositeB>( "child of child" ); std::shared_ptr<Component> c4 = std::make_shared<ConcreteCompositeA>( 107 ); std::shared_ptr<Component> c5 = std::make_shared<ConcreteCompositeB>( "other child" ); c3->Add( c4 ); c2->Add( c3 ); c1->Add( c2 ); c1->Add( c5 ); c1->Execute( 0 ); getch(); return 0; } |
出力結果。
—————————————-
A: val == 3
B: str == child
B: str == child of child
A: val == 107
B: str == other child
—————————————-
生ポインタを使うのと比べ、解放処理が不要で便利。