Слабые указатели похожи на умные указатели, за исключением того, что ссылки со слабых
указатели не мешают сборке мусора, а слабые указатели должны
проверить их действительность, прежде чем они будут использованы.
В нашем проекте (Linderdaum Engine http://www.linderdaum.com) мы используем навязчивые указатели. Чтобы избежать циклических ссылок и островков изоляции, мы внедрили слабые навязчивые указатели следующим образом:
namespace LPtr
{
clPtr<iObject> GetObjectsGraphPtrWrapper( sEnvironment* Env, iObject* Obj, size_t Generation );
};
/// Intrusive weak smart pointer
template <class T> class clWeakPtr
{
public:
/// default constructor
clWeakPtr(): Env( NULL ), FObject( NULL ), FGeneration( 0 ) {}
explicit clWeakPtr( T* Ptr )
: Env( Ptr ? Ptr->Env : NULL )
, FObject( Ptr )
, FGeneration( Ptr ? Ptr->FGeneration : 0 ) {}
explicit clWeakPtr( const clPtr<T>& Ptr )
: Env( Ptr ? Ptr->Env : NULL )
, FObject( Ptr.GetInternalPtr() )
, FGeneration( Ptr ? Ptr->FGeneration : 0 ) {}
clPtr<T> Lock() const
{
clPtr<iObject> P = LPtr::GetObjectsGraphPtrWrapper( Env, FObject, FGeneration );
return P.DynamicCast<T>();
}
private:
sEnvironment* Env;
T* FObject;
size_t FGeneration;
};
GetObjectsGraphPtrWrapper
здесь только ради предварительных деклараций и делает примерно это:
LMutex Lock( &FObjectsGraphMutex );
clObjectsGraph::const_iterator i = std::find( Env->ObjectsGraph.begin(), Env->ObjectsGraph.end(), Obj );
if ( i == Env->ObjectsGraph.end() ) return clPtr<iObject>();
bool IsSame = Obj->FGeneration == Generation;
bool IsAlive = Obj->GetReferenceCounter() > 0;
return ( IsSame && IsAlive ) ? clPtr<iObject>( Obj ) : clPtr<iObject>();
Generation
является глобальным в сфере sEnvironment
и атомарно увеличивается каждый раз, когда создается новый объект.
Мои вопросы:
1) Безопасно ли реализовывать подобные слабые ссылки?
2) Есть ли способы оптимизировать clWeakPtr::Lock()
?
1) Это кажется действительно безопасным, но любая модификация графика будет иметь некоторые противоречия с LPtr::GetObjectsGraphPtrWrapper
2) может помочь блокировка чтения-записи, по крайней мере, вы сможете вызвать несколько Lock()
в параллели
Проблема с вашим решением состоит в том, что он побеждает местность, которую приносят неинтрузивные слабые указатели.
В зависимости от уровня параллелизма, это может стать проблемой, так как каждый вызов Lock()
предотвратит любое создание объекта, а также любой другой Lock()
вызов без блокировки чтения-записи.
Других решений пока нет …