Как удалить умные указатели из кеша, когда больше нет ссылок? - PullRequest
7 голосов
/ 05 марта 2010

Я пытался использовать умные указатели для обновления существующего приложения, и я пытаюсь преодолеть загадку. В моем приложении есть кеш объектов, например, давайте называть их книгами. Теперь этот кеш книг запрашивается по ID, и если они находятся в кеше, они возвращаются, если не объект запрашивается из внешней системы (медленная работа) и добавляется в кеш. Как только в кеше в приложении можно открыть много окон, каждое из этих окон может ссылаться на книгу. В предыдущей версии приложения программист должен был поддерживать AddRef и Release, когда каждое окно, использующее объект Book, было закрыто, финальная версия (в диспетчере кэша) удаляла объект из кэша и удаляла объект.

Возможно, вы заметили слабое звено в цепочке, это, конечно, программист, который не забывает вызывать 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

Приветствие Рич

Ответы [ 4 ]

9 голосов
/ 05 марта 2010

Я никогда не использовал интеллектуальные указатели boost :: intrusive, но если бы вы использовали интеллектуальные указатели shared_ptr, вы могли бы использовать объекты слабой_птр для своего кэша.

Эти указатели weak_ptr не считаются ссылками, когда система решает освободить их память, но могут использоваться для получения shared_ptr, если объект еще не удален.

4 голосов
/ 05 марта 2010

Вы можете использовать boost shared_ptr . При этом вы можете предоставить пользовательское удаление (см. этот SO поток, как это сделать). И в этом пользовательском удалителе вы знаете, что вы достигли последнего счетчика ссылок. Теперь вы можете удалить указатель из кэша.

1 голос
/ 05 марта 2010

Вам нужно хранить в своем кэше слабые указатели вместо shared_ptr.

0 голосов
/ 05 марта 2010

Вы можете написать intrusive_weak_ptr для своего класса кэша. Вам все равно придется что-то делать, чтобы время от времени очищать устаревшие слабые указатели в вашем кэше, но это не так важно, как очистка реальных кэшированных объектов.

http://lists.boost.org/boost-users/2008/08/39563.php - это реализация, которая была размещена в списке рассылки Boost. Это не потокобезопасно, но может работать для вас.

...