Я пытался использовать интеллектуальные указатели для обновления существующего приложения, и я пытаюсь решить загадку. В моем приложении у меня есть кеш объектов, например, я могу называть их книгами. Теперь этот кеш книг запрашивается по идентификатору, и если они находятся в кеше, они возвращаются, если нет, объект запрашивается из внешней системы (медленная работа) и добавляется в кеш. Попав в кеш, в приложении можно открыть множество окон, каждое из которых может принимать ссылку на книгу. В предыдущей версии приложения программист должен был поддерживать AddRef и Release, когда каждое окно, использующее объект Book, было закрыто, последний Release (в диспетчере кеша) удалял объект из кеша и удалял объект.
Возможно, вы заметили здесь слабое звено в цепочке, конечно, программист не забыл вызвать AddRef и Release. Теперь я перешел на интеллектуальные указатели (boost :: intrusive). Мне больше не нужно беспокоиться о вызовах AddRef и Release. Однако это приводит к проблеме, кеш имеет ссылку на объект, поэтому, когда последнее окно закрывается, кеш не уведомляется о том, что никто другой не держит ссылку.
Моей первой мыслью было периодически обходить кеш и очищать объекты со счетчиком ссылок, равным единице. Мне не понравилась эта идея, так как это была операция по Приказу 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
Ура, Рич
BaseObject
некопируемым (объявив, но не определив частный конструктор копирования и оператор присваивания), или сделать его корректно копируемым каким-либо образом. Точно так жеBook
имеет опасную семантику копирования, которую лучше всего исправить, используяstd::string
вместо массива, управляемого вручную. - person Mike Seymour   schedule 05.03.2010