私は既存のアプリをアップグレードするためにスマートポインターを使用しようとしてきました、そして私はパズルを克服しようとしています。私のアプリにはオブジェクトのキャッシュがあります。たとえば、それらを本と呼びましょう。これで、この本のキャッシュはIDによって要求され、キャッシュ内にある場合は返されます。そうでない場合は、オブジェクトが外部システムから要求され(低速操作)、キャッシュに追加されます。キャッシュに入ると、アプリで多くのウィンドウを開くことができ、これらの各ウィンドウは本への参照を取得できます。以前のバージョンのアプリでは、プログラマーはAddRefとReleaseを維持する必要があり、Bookオブジェクトを使用するすべてのウィンドウが閉じられると、(キャッシュマネージャー上の)最終リリースでオブジェクトがキャッシュから削除され、オブジェクトが削除されました。
ここでチェーンの弱いリンクを見つけたかもしれません。もちろん、プログラマーはAddRefとReleaseを呼び出すことを覚えています。これで、スマートポインター(boost :: intrusive)に移動しました。AddRefとReleaseの呼び出しについて心配する必要はありません。ただし、これは問題につながります。キャッシュにはオブジェクトへの参照があるため、最後のウィンドウを閉じても、他の誰も参照を保持していないことがキャッシュに通知されません。
私の最初の考えは、定期的にキャッシュをウォークし、参照カウントが1のオブジェクトをパージすることでした。これはオーダーNの操作であり、気分が悪いため、私はこのアイデアが気に入らなかった。私はコールバックシステムを思いついた。それはより良いが素晴らしいものではない。コールバックシステムのコードを含めましたが、誰かがこれを行うためのより良い方法があるかどうか疑問に思っていましたか?
class IContainer
{
public:
virtual void FinalReference(BaseObject *in_obj)=0;
};
class BaseObject
{
unsigned int m_ref;
public:
IContainer *m_container;
BaseObject() : m_ref(0),m_container(0)
{
}
void AddRef()
{
++m_ref;
}
void Release()
{
// if we only have one reference left and we have a container
if( 2 == m_ref && 0 != m_container )
{
m_container->FinalReference(this);
}
if( 0 == (--m_ref) )
{
delete this;
}
}
};
class Book : public BaseObject
{
char *m_name;
public:
Book()
{
m_name = new char[30];
sprintf_s(m_name,30,"%07d",rand());
}
~Book()
{
cout << "Deleting book : " << m_name;
delete [] m_name;
}
const char *Name()
{
return m_name;
}
};
class BookList : public IContainer
{
public:
set<BookIPtr> m_books;
void FinalReference(BaseObject *in_obj)
{
set<BookIPtr>::iterator it = m_books.find(BookIPtr((Book*)in_obj));
if( it != m_books.end() )
{
in_obj->m_container = 0;
m_books.erase( it );
}
}
};
namespace boost
{
inline void intrusive_ptr_add_ref(BaseObject *p)
{
// increment reference count of object *p
p->AddRef();
}
inline void intrusive_ptr_release(BaseObject *p)
{
// decrement reference count, and delete object when reference count reaches 0
p->Release();
}
} // namespace boost
乾杯リッチ