リファレンスカウンタの真価
さて、まずは前回の宿題への回答から行きましょう
前回の宿題は次の3つでしたね
※1
- それより、自分で設計する時にはこの例で使っている GetClassObjectみたいな仕様にしちゃダメですよ。これはただ例のために使ってるだけですからね。
- どういったところが問題なのか、これは次回への宿題にしましょう。
※2
- それから、この CSmartObject<>というテンプレートクラスの使い方には制限がありますよ。
- どんな制限なのか、これも次回への宿題にしましょうか。
※3
- あー。もう1つ。
- クラスとクラス識別子の対応は一意なわけですから CSmartObject
っていう書き方も冗長ですよね。 - できれば CSmartObject
で済ませたいですよね。 - これもあわせて次回への宿題にしましょう。
まずは他と関係のない※3から見てみましょう
前回の例においてクラス識別子とクラスの対応は一意ということにしてあります
というわけで CSmartObject<>を使う時に毎回 CSmartObject
これをどうにかして CSmartObject
できます
これは答えを見るだけで分かるでしょう。
Fig.1
template<struct T> struct classidof { static inline OBJECT_CLASS_ID get_classid(); }; template<> struct classidof<IDerivedObject1*> { static inline OBJECT_CLASS_ID get_classid(){ return classid_of_IDerivedObject1; } }; template<> struct classidof<IDerivedObject2*> { static inline OBJECT_CLASS_ID get_classid(){ return classid_of_IDerivedObject2; } }; template<> struct classidof<IDerivedObject3*> { static inline OBJECT_CLASS_ID get_classid(){ return classid_of_IDerivedObject3; } }; #define classidof( T ) classidof<T>::get_classid() template<class T> class CSmartObject { IBaseObject* m_pBaseObject; T m_pDerivedObject; void Assign( IBaseObject* pBaseObjectNew ){ if( pBaseObjectNew != m_pBaseObject ){ Release(); m_pBaseObject = pBaseObjectNew; if( m_pBaseObject && m_pBaseObject->QuerySupport( classidof( T ))){ m_pDerivedObject = ( T )m_pBaseObject->GetClassObject( classidof( T )); } } } void Release(){ if( m_pDerivedObject &&( m_pBaseObject != m_pDerivedObject )){ m_pDerivedObject->DeleteMe(); m_pDerivedObject = NULL; } m_pBaseObject = NULL; } public: CSmartObject& operator=( IBaseObject* pBaseObject ){ Assign( pBaseObject ); return *this; } T operator->() const { return m_pDerivedObject; } operator T() const { return m_pDerivedObject; } CSmartObject( IBaseObject* pBaseObject = NULL ): m_pBaseObject(NULL), m_pDerivedObject(NULL) { Assign( pBaseObject ); } ~CSmartObject(){ Release(); } };
テンプレートのパラメータにクラス識別子を毎回指定する代わりに指定されたクラスから対応するクラス識別子を導く別のテンプレートクラスを用意してあげればよかったんですね
見た目はテンプレートなんて使っているので何か新しい感じがするかもしれませんが、要するに問題を単純な単位に分割するという昔ながらの方法です
さてでは次に※1をとばして、※2へ行きましょうか
※2の CSmartObject<>を使用する上での制限とは何でしょうか
答えから書いてしまうと、その制限とは CSmartObject<>同士の初期化/代入ができないということです
つまりFig.2のような使い方はできないということです
Fig.2
CSmartObject<IDerivedObject1*> pDerivedObject1_1 = pBaseObject; CSmartObject<IDerivedObject1*> pDerivedObject1_2 = pDerivedObject1_1;
ここで pBaseObject の実装を考えてみましょう
IBaseObject::GetClassObject は要求されたクラスをサポートする場合、内部で新しいインスタンスを作る場合があるという仕様でしたね
この内部で新しいインスタンスを作るという仕様には、2通りの実装が考えられることが分かると思います
1つは、新たに作ったインスタンスを pBaseObject の中に持つというもの。もう1つは、新しく作ったインスタンスを内部には持たないというものですね
ただしもう1つの仕様として、新しく作られたインスタンスを解放する責任は呼び出し側にあるというものがありますから、IBaseObject::GetClassObjectの実装としてその内部に新しく作ったインスタンスを持つことはできないということがわかりますね
さて、ではこのとき Fig.2のように書いたとしたら何が起こるのでしょうか
CSmartObject<>にはコピーコンストラクタの定義がありませんから pDerivedObject1_2 の初期化ではデフォルトのコピーコンストラクタが呼び出されますね
デフォルトのコピーコンストラクタは単純に全てのビットをコピーするだけですから、pDerivedObject1_1 が内部に持つオブジェクトの参照(ポインタ)と pDerivedObject1_2 が内部にもつオブジェクトの参照(ポインタ)は同じオブジェクトを指すことになります
これが問題です
ここでどちらか片方のデストラクタが呼び出されるとどうでしょうか
CSmartObject<>のデストラクタでは IBaseObject::GetClassObjectから貰ったインスタンスを正しく解放しています
CSmartObject<>が1つのときはそれでいいのですが、しかし、今は同時に2つありますから片方のデストラクタが勝手にオブジェクトを解放してしまうと、もう片方が指しているオブジェクトの参照(ポインタ)が無効になってしまいますね
ということで、CSmartObject<>は同時に2つのインスタンスを作れるようには設計されていないと言うことが分かると思います。つまり※2の制限が生まれるわけです
このような制限がある場合、コンパイル時に検出できるようにコピーコンストラクタを private に置くといいでしょう
では次に※1を考えてみましょう
IBaseObject::GetClassObjectの仕様はこうでしたね
- クラス識別子で指定された派生クラスのインターフェイスをサポートするならその派生クラスのインスタンス(を指すポインタ)を返す。サポートしないなら NULLを返す
- このとき返される派生クラスのインスタンスのアドレスが基底クラスのインスタンスのアドレスと同じでなかったら、(それは内部で新たにアロケートしたものということで、)呼び出し側は責任を持ってその派生クラスのインスタンスを解放する必要がある
どうしてこのような仕様はいけないのでしょう
それは、内部でインスタンスを新たに確保するかどうか、そのインスタンスのアドレスがどうこう、というのは純粋に実装上の問題だからです
実装上の問題というならばそんなものをインターフェイスの仕様とすべきではないということです
この IBaseObject は、折角「ABC」を使って実装の詳細を隠しているにも関わらず、インターフェイスの仕様に実装の詳細が顔を出してしまっているのです
さて、では実際、どうすべきなのでしょうか
この例の場合では、このメソッド(GetClassObject)を使う側にインスタンスを解放すべきかどうかを判断させることを任せるべきではありません
ここでは解放させるなら常に解放させるようにすべきなのです
もうお分かりと思いますが、それはリファレンスカウンタを導入すれば解決できますね
オブジェクトがリファレンスカウンタ付きであれば、使う側は GetClassObjectで得たオブジェクトを使い終わったら何も考えずにいつも解放すればいいのです
リファレンスカウンタを導入した IBaseObjectは Fig.3のようになりますね
Fig.3
struct IBaseObject { virtual void AddRef() = 0; virtual void Release() = 0; virtual bool QuerySupport( OBJECT_CLASS_ID clsid ) = 0; virtual IBaseObject* GetClassObject( OBJECT_CLASS_ID clsid ) = 0; ・・・ };
これを反映させて CSmartObject<>も修正してみましょう。
Fig.4
template<class T> class CSmartObject { IBaseObject* m_pBaseObject; T m_pDerivedObject; void Assign( IBaseObject* pBaseObjectNew ){ Release(); if( pBaseObjectNew ){ pBaseObjectNew->AddRef(); } m_pBaseObject = pBaseObjectNew; if( m_pBaseObject && m_pBaseObject->QuerySupport( classidof( T ))){ m_pDerivedObject = ( T )m_pBaseObject->GetClassObject( classidof( T )); } } void Release(){ if( m_pDerivedObject ){ m_pDerivedObject->Release(); m_pDerivedObject = NULL; } if( m_pBaseObject ){ m_pBaseObject->Release(); m_pBaseObject = NULL; } } public: CSmartObject& operator=( IBaseObject* pBaseObject ){ Assign( pBaseObject ); return *this; } T operator->() const { return m_pDerivedObject; } operator T() const { return m_pDerivedObject; } CSmartObject( IBaseObject* pBaseObject = NULL ): m_pBaseObject(NULL), m_pDerivedObject(NULL) { Assign( pBaseObject ); } ~CSmartObject(){ Release(); } };
IBaseObjectにリファレンスカウンタを導入したので m_pBaseObject と m_pDerivedObject に値を代入するところに AddRef/Releaseが挿入されたんですね
さて、すると前回のFig.5はどうなるでしょうか
それは Fig.5のようになるでしょうか
Fig.5
for( int n = 0; n < pParentObject->GetNumberOfChildren(); ++n ){ IBaseObject* pBaseObject = pParentObject->GetChildObject( n ); if( pBaseObject->QuerySupport( classid_of_IDerivedObject1 )){ IDerivedObject1* pDerivedObject1 = ( IDerivedObject1* )pBaseObject->GetClassObject( classid_of_IDerivedObject1 ); BOOL fSuccess = pDerivedObject1->DoSomething1( ・・・ ); // 使い終わったら必ず解放する。 pDerivedObject1->Release(); if( fSuccess ){ break; // 失敗したら中断する。 } } }
これでいいでしょうか
ダメですね
よく見ると pBaseObject は自分の関知しないスコープから貰ったものですから、これも使い終わったら解放してあげないといけませんよね。
Fig.6
for( int n = 0; n < pParentObject->GetNumberOfChildren(); ++n ){ IBaseObject* pBaseObject = pParentObject->GetChildObject( n ); IDerivedObject1* pDerivedObject1 = ( IDerivedObject1* )pBaseObject->GetClassObject( classid_of_IDerivedObject1 ); pBaseObject->Release(); // 使い終わったらこれも解放する。 if( pDerivedObject1 ){ BOOL fSuccess = pDerivedObject1->DoSomething1( ・・・ ); // 使い終わったら必ず解放する。 pDerivedObject1->Release(); if( fSuccess ){ break; // 失敗したら中断する。 } } }
なんか汚いですね。こんなことならリファレンスカウンタなんて導入しない方がマシだったでしょうか?
確かにリファレンスカウンタにはスコープとオブジェクトの参照(ポインタ)の管理という煩雑な面があります
しかしそれはコンパイラに任せられる程度の煩雑さです
Fig.6は CSmartObject<> を使うと Fig.7のようにすっきり書けますね
Fig.7
for( int n = 0; n < pParentObject->GetNumberOfChildren(); ++n ){ CSmartObject<IDerivedObject1*> pDerivedObject1 = pParentObject->GetChildObject( n ); if( pDerivedObject1 && pDerivedObject1->DoSomething1( ・・・ )){ break; // 失敗したら中断する。 } }
これは前回の Fig.7と変わりありませんね
さあ。しかしこの CSmartObject<> には使用上の制限があるのでした
制限に気を使いながら使うのも面倒そうですか?
でもちょっと待ってください。ここでFig.4の修正を踏まえて再び※2について考えてみましょう
今度はどうでしょうか
Fig.4のように修正してもやはりデフォルトのコピーコンストラクタが働いてオブジェクトの参照(ポインタ)をコピーしてしまうとダメなのは同じですね
しかし、もう少し考えてみましょう
今や IBaseObject はリファレンスカウンタの機能を持っていますから、デフォルトのコピーコンストラクタが pBaseObjectの内部に持っているオブジェクトの参照(ポインタ)をコピーしたら一緒にそのオブジェクトの参照カウントも増やしてあげないといけませんよね
オブジェクトの参照(ポインタ)のコピーが増えたら参照カウントも増やしてあげる。というのが決まりですからね
ということはどういうことでしょう
そうです。コピーコンストラクタをデフォルトのまま使ってはいけないということですね
では CSmartObject<> に正しいコピーコンストラクタを追加して試してみましょう
Fig.8
template<class T> class CSmartObject { IBaseObject* m_pBaseObject; T m_pDerivedObject; void Assign( IBaseObject* pBaseObjectNew ){ Release(); if( pBaseObjectNew ){ pBaseObjectNew->AddRef(); } m_pBaseObject = pBaseObjectNew; if( m_pBaseObject && m_pBaseObject->QuerySupport( classidof( T ))){ m_pDerivedObject = ( T )m_pBaseObject->GetClassObject( classidof( T )); } } void Release(){ if( m_pDerivedObject ){ m_pDerivedObject->Release(); m_pDerivedObject = NULL; } if( m_pBaseObject ){ m_pBaseObject->Release(); m_pBaseObject = NULL; } } public: CSmartObject& operator=( IBaseObject* pBaseObject ){ Assign( pBaseObject ); return *this; } CSmartObject& operator=( CSmartObject& rhs ){ Assign( rhs.m_pBaseObject ); return *this; } T operator->() const { return m_pDerivedObject; } operator T() const { return m_pDerivedObject; } CSmartObject( IBaseObject* pBaseObject = NULL ): m_pBaseObject(NULL), m_pDerivedObject(NULL) { Assign( pBaseObject ); } CSmartObject( CSmartObject& rhs ): m_pBaseObject(NULL), m_pDerivedObject(NULL) { Assign( rhs.m_pBaseObject ); } ~CSmartObject(){ Release(); } };
さあ、するとどうでしょうか
今度はいいですね
※1の問題を解決したら※2の制限もなくなってしまいました
実は※2の問題と言うのは※1の設計上の問題からくる制約だったんですね
もう少し具体的に考えてみるために IDerivedObject1,2,3をサポートする IBaseObject互換オブジェクトをリファレンスカウンタがない場合(Fig.9)とある場合(Fig.10)の2通り実装してみましょう
Fig.9
class CDerivedObject1 : public IBaseObject { public: virtual void DeleteMe(){ delete this; } virtual BOOL QuerySupport( OBJECT_CLASS_ID clsid ){ return ( clsid == classid_of_IDerivedObject1 ); } virtual IBaseObject* GetClassObject( OBJECT_CLASS_ID clsid ){ IBaseObject* pObject = NULL; if( clsid == classid_of_IDerivedObject1 ){ pObject = this; } return pObject; } virtual BOOL DoSomething1(){ // TODO: } CDerivedObject1() { } virtual ~CDerivedObject1(){ } }; class CDerivedObject2 : public IBaseObject { public: virtual void DeleteMe(){ delete this; } virtual BOOL QuerySupport( OBJECT_CLASS_ID clsid ){ return ( clsid == classid_of_IDerivedObject2 ); } virtual IBaseObject* GetClassObject( OBJECT_CLASS_ID clsid ){ IBaseObject* pObject = NULL; if( clsid == classid_of_IDerivedObject2 ){ pObject = this; } return pObject; } virtual BOOL DoSomething2(){ // TODO: } CDerivedObject2() { } virtual ~CDerivedObject2(){ } }; class CDerivedObject3 : public IBaseObject { public: virtual void DeleteMe(){ delete this; } virtual BOOL QuerySupport( OBJECT_CLASS_ID clsid ){ return ( clsid == classid_of_IDerivedObject3 ); } virtual IBaseObject* GetClassObject( OBJECT_CLASS_ID clsid ){ IBaseObject* pObject = NULL; if( clsid == classid_of_IDerivedObject3 ){ pObject = this; } return pObject; } virtual BOOL DoSomething3(){ // TODO: } CDerivedObject3() { } virtual ~CDerivedObject3(){ } }; class CBaseObject123 : public IBaseObject { public: virtual void DeleteMe(){ delete this; } virtual BOOL QuerySupport( OBJECT_CLASS_ID clsid ){ return (( clsid == classid_of_IDerivedObject1 )||( clsid == classid_of_IDerivedObject2 )||( clsid == classid_of_IDerivedObject3 )); } virtual IBaseObject* GetClassObject( OBJECT_CLASS_ID clsid ){ IBaseObject* pObject = NULL; if( clsid == classid_of_IDerivedObject1 ){ pObject = new CDerivedObject1; } else if( clsid == classid_of_IDerivedObject2 ){ pObject = new CDerivedObject2; } else if( clsid == classid_of_IDerivedObject3 ){ pObject = new CDerivedObject3; } return pObject; } CBaseObject123() { } virtual ~CBaseObject123(){ } };
Fig.10
class CDerivedObject1 : public IBaseObject { ULONG m_cRef; public: void AddRef(){ ++m_cRef; } void Release(){ if( --m_cRef == 0 ){ delete this; } } virtual BOOL QuerySupport( OBJECT_CLASS_ID clsid ){ return ( clsid == classid_of_IDerivedObject1 ); } virtual IBaseObject* GetClassObject( OBJECT_CLASS_ID clsid ){ IBaseObject* pObject = NULL; if( clsid == classid_of_IDerivedObject1 ){ AddRef(); pObject = this; } return pObject; } virtual BOOL DoSomething1(){ // TODO: } CDerivedObject1(): m_cRef(1) { } virtual ~CDerivedObject1(){ } }; class CDerivedObject2 : public IBaseObject { ULONG m_cRef; public: void AddRef(){ ++m_cRef; } void Release(){ if( --m_cRef == 0 ){ delete this; } } virtual BOOL QuerySupport( OBJECT_CLASS_ID clsid ){ return ( clsid == classid_of_IDerivedObject2 ); } virtual IBaseObject* GetClassObject( OBJECT_CLASS_ID clsid ){ IBaseObject* pObject = NULL; if( clsid == classid_of_IDerivedObject2 ){ AddRef(); pObject = this; } return pObject; } virtual BOOL DoSomething2(){ // TODO: } CDerivedObject2(): m_cRef(1) { } virtual ~CDerivedObject2(){ } }; class CDerivedObject3 : public IBaseObject { ULONG m_cRef; public: void AddRef(){ ++m_cRef; } void Release(){ if( --m_cRef == 0 ){ delete this; } } virtual BOOL QuerySupport( OBJECT_CLASS_ID clsid ){ return ( clsid == classid_of_IDerivedObject3 ); } virtual IBaseObject* GetClassObject( OBJECT_CLASS_ID clsid ){ IBaseObject* pObject = NULL; if( clsid == classid_of_IDerivedObject3 ){ AddRef(); pObject = this; } return pObject; } virtual BOOL DoSomething3(){ // TODO: } CDerivedObject3(): m_cRef(1) { } virtual ~CDerivedObject3(){ } }; class CBaseObject123 : public IBaseObject { ULONG m_cRef; IBaseObject* m_pDerivedObject1; IBaseObject* m_pDerivedObject2; IBaseObject* m_pDerivedObject3; public: void AddRef(){ ++m_cRef; } void Release(){ if( --m_cRef == 0 ){ delete this; } } virtual BOOL QuerySupport( OBJECT_CLASS_ID clsid ){ return (( clsid == classid_of_IDerivedObject1 )||( clsid == classid_of_IDerivedObject2 )||( clsid == classid_of_IDerivedObject3 )); } virtual IBaseObject* GetClassObject( OBJECT_CLASS_ID clsid ){ IBaseObject* pObject = NULL; if( clsid == classid_of_IDerivedObject1 ){ if( !m_pDerivedObject1 ){ m_pDerivedObject1 = new CDerivedObject1; } m_pDerivedObject1->AddRef(); pObject = m_pDerivedObject1; } else if( clsid == classid_of_IDerivedObject2 ){ if( !m_pDerivedObject2 ){ m_pDerivedObject2 = new CDerivedObject2; } m_pDerivedObject2->AddRef(); pObject = m_pDerivedObject2; } else if( clsid == classid_of_IDerivedObject3 ){ if( !m_pDerivedObject3 ){ m_pDerivedObject3 = new CDerivedObject3; } m_pDerivedObject3->AddRef(); pObject = m_pDerivedObject3; } return pObject; } CBaseObject123(): m_cRef(1), m_pDerivedObject1(NULL), m_pDerivedObject2(NULL), m_pDerivedObject3(NULL) { } virtual ~CBaseObject123(){ if( m_pDerivedObject3 ){ m_pDerivedObject3->Release(); } if( m_pDerivedObject2 ){ m_pDerivedObject2->Release(); } if( m_pDerivedObject1 ){ m_pDerivedObject1->Release(); } } };
さてさて、こうやって両方書いてみましたがリファレンスカウンタのある/なしで違うところと言っても、ただ単に CBaseObject123の内部にオブジェクトの参照(ポインタ)を持っているかどうかだけで、宿題のところで説明した点以外にはあんまりメリットがなさそうに見えますか?
でもそうではないんですね。リファレンスカウンタを導入したことによって、CBaseObject123の内部にオブジェクトの参照(ポインタ)を持てるようになったという点が非常に大きいんです
このことによって Fig.11のようにも書けるようになったのです
Fig.11
class CDerivedObject1 : public IBaseObject { IBaseObject* m_pBaseObject; ULONG m_cRef; public: void AddRef(){ ++m_cRef; } void Release(){ if( --m_cRef == 0 ){ delete this; } } virtual BOOL QuerySupport( OBJECT_CLASS_ID clsid ){ BOOL fSupported = FALSE; if( clsid == classid_of_IDerivedObject1 ){ fSupported = TRUE; } else if( m_pBaseObject ){ fSupported = m_pBaseObject->QuerySupport( clsid ); } return fSupported; } virtual IBaseObject* GetClassObject( OBJECT_CLASS_ID clsid ){ IBaseObject* pObject = NULL; if( clsid == classid_of_IDerivedObject1 ){ AddRef(); pObject = this; } else if( m_pBaseObject ){ pObject = m_pBaseObject->GetClassObject( clsid ); } return pObject; } virtual BOOL DoSomething1(){ // TODO: } CDerivedObject1( IBaseObject* pBaseObject = NULL ): m_pBaseObject(pBaseObject), m_cRef(1) { } virtual ~CDerivedObject1(){ } }; class CDerivedObject2 : public IBaseObject { IBaseObject* m_pBaseObject; ULONG m_cRef; public: void AddRef(){ ++m_cRef; } void Release(){ if( --m_cRef == 0 ){ delete this; } } virtual BOOL QuerySupport( OBJECT_CLASS_ID clsid ){ BOOL fSupported = FALSE; if( clsid == classid_of_IDerivedObject2 ){ fSupported = TRUE; } else if( m_pBaseObject ){ fSupported = m_pBaseObject->QuerySupport( clsid ); } return fSupported; } virtual IBaseObject* GetClassObject( OBJECT_CLASS_ID clsid ){ IBaseObject* pObject = NULL; if( clsid == classid_of_IDerivedObject2 ){ AddRef(); pObject = this; } else if( m_pBaseObject ){ pObject = m_pBaseObject->GetClassObject( clsid ); } return pObject; } virtual BOOL DoSomething2(){ // TODO: } CDerivedObject2( IBaseObject* pBaseObject = NULL ): m_pBaseObject(pBaseObject), m_cRef(1) { } virtual ~CDerivedObject2(){ } }; class CDerivedObject3 : public IBaseObject { IBaseObject* m_pBaseObject; ULONG m_cRef; public: void AddRef(){ ++m_cRef; } void Release(){ if( --m_cRef == 0 ){ delete this; } } virtual BOOL QuerySupport( OBJECT_CLASS_ID clsid ){ BOOL fSupported = FALSE; if( clsid == classid_of_IDerivedObject3 ){ fSupported = TRUE; } else if( m_pBaseObject ){ fSupported = m_pBaseObject->QuerySupport( clsid ); } return fSupported; } virtual IBaseObject* GetClassObject( OBJECT_CLASS_ID clsid ){ IBaseObject* pObject = NULL; if( clsid == classid_of_IDerivedObject3 ){ AddRef(); pObject = this; } else if( m_pBaseObject ){ pObject = m_pBaseObject->GetClassObject( clsid ); } return pObject; } virtual BOOL DoSomething3(){ // TODO: } CDerivedObject3( IBaseObject* pBaseObject = NULL ): m_pBaseObject(pBaseObject), m_cRef(1) { } virtual ~CDerivedObject3(){ } }; class CBaseObject123 : public IBaseObject { ULONG m_cRef; IBaseObject* m_pDerivedObject1; IBaseObject* m_pDerivedObject2; IBaseObject* m_pDerivedObject3; public: void AddRef(){ ++m_cRef; } void Release(){ if( --m_cRef == 0 ){ delete this; } } virtual BOOL QuerySupport( OBJECT_CLASS_ID clsid ){ return (( clsid == classid_of_IDerivedObject1 )||( clsid == classid_of_IDerivedObject2 )||( clsid == classid_of_IDerivedObject3 )); } virtual IBaseObject* GetClassObject( OBJECT_CLASS_ID clsid ){ IBaseObject* pObject = NULL; if( clsid == classid_of_IDerivedObject1 ){ if( !m_pDerivedObject1 ){ m_pDerivedObject1 = new CDerivedObject1( this ); } m_pDerivedObject1->AddRef(); pObject = m_pDerivedObject1; } else if( clsid == classid_of_IDerivedObject2 ){ if( !m_pDerivedObject2 ){ m_pDerivedObject2 = new CDerivedObject2( this ); } m_pDerivedObject2->AddRef(); pObject = m_pDerivedObject2; } else if( clsid == classid_of_IDerivedObject3 ){ if( !m_pDerivedObject3 ){ m_pDerivedObject3 = new CDerivedObject3( this ); } m_pDerivedObject3->AddRef(); pObject = m_pDerivedObject3; } return pObject; } CBaseObject123(): m_cRef(1), m_pDerivedObject1(NULL), m_pDerivedObject2(NULL), m_pDerivedObject3(NULL) { } virtual ~CBaseObject123(){ if( m_pDerivedObject3 ){ m_pDerivedObject3->Release(); } if( m_pDerivedObject2 ){ m_pDerivedObject2->Release(); } if( m_pDerivedObject1 ){ m_pDerivedObject1->Release(); } } };
クラス間に親子関係を持たせることによってコードが少し複雑になったというだけのことではないですよ
こうすることによって Fig.12のような使い方が可能になったのです
Fig.12
CSmartObject<IDerivedObject1*> pDerivedObject1 = pBaseObject; pDerivedObject1->DoSomething1( ・・・ ); CSmartObject<IDerivedObject1*> pDerivedObject1_2 = pDerivedObject1; pDerivedObject1_2->DoSomething1( ・・・ ); CSmartObject<IDerivedObject2*> pDerivedObject2 = pDerivedObject1_2; pDerivedObject2->DoSomething2( ・・・ ); CSmartObject<IDerivedObject3*> pDerivedObject3 = pDerivedObject2; pDerivedObject3->DoSomething3( ・・・ );
どうでしょうか。リファレンスカウンタがなかったときには考えられなかった程の柔軟性を獲得しましたね
これはほんの一例に過ぎませんが、こういった点こそ正にリファレンスカウンタの真価なのです
というところで今回はここまでにしましょう。ではまた次回