2010-03-05 8 views
7

He estado tratando de usar punteros inteligentes para actualizar una aplicación existente, y estoy tratando de superar un acertijo. En mi aplicación tengo un caché de objetos, por ejemplo, vamos a llamarlos libros. Ahora, esta caché de libros se solicita por ID y, si están en la memoria caché, se devuelven; de lo contrario, el objeto se solicita desde un sistema externo (operación lenta) y se agrega a la memoria caché. Una vez en la memoria caché, se pueden abrir muchas ventanas en la aplicación, cada una de estas ventanas puede tomar una referencia al libro. En la versión anterior de la aplicación, el programador tenía que mantener AddRef y Release, cuando cada ventana que utilizaba el objeto Book estaba cerrada, la Versión final (en el administrador de caché) eliminaba el objeto de la memoria caché y eliminaba el objeto.¿Cómo eliminar punteros inteligentes de un caché cuando ya no hay más referencias?

Es posible que haya detectado el eslabón débil de la cadena aquí, por supuesto, el programador recuerda llamar a AddRef y Release. Ahora me he movido a punteros inteligentes (boost :: intrusive) Ya no tengo que preocuparme por llamar a AddRef y Release. Sin embargo, esto genera un problema, la memoria caché tiene una referencia al objeto, por lo que cuando se cierra la ventana final, no se notifica a la memoria caché que nadie más está sosteniendo una referencia.

Mis primeros pensamientos fueron caminar periódicamente la caché y purgar objetos con un recuento de referencia de uno. No me gustó esta idea, ya que era una operación de la Orden N y no me sentía bien. He creado un sistema de devolución de llamada, que es mejor pero no fantástico. He incluido el código para el sistema de devolución de llamada, sin embargo, me preguntaba si alguien tenía una mejor manera de hacerlo.

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 

Saludos Rich

+0

Debe hacer que 'BaseObject' no se pueda copiar (declarando, pero sin definir, un constructor de copia privada y un operador de asignación), o hacer que se pueda copiar correctamente de alguna manera. Del mismo modo, 'Book' tiene una semántica de copia peligrosa, que se arregla mejor usando' std :: string' en lugar de una matriz administrada manualmente. –

+0

virtual dtor por favor !!! – curiousguy

Respuesta

9

nunca he usado impulso :: intrusiva punteros inteligentes, pero si desea utilizar punteros inteligentes shared_ptr, podría utilizar weak_ptr objetos de la memoria caché.

Esos punteros weak_ptr no cuentan como referencia cuando el sistema decide liberar su memoria, pero se pueden usar para recuperar un shared_ptr mientras el objeto aún no se haya eliminado.

+0

Tengo que usar punteros intrusivos ya que la base de códigos es enorme y está muy interconectada. No puedo cambiar el tipo de base a un puntero inteligente. También tengo que poder cambiar un puntero existente en un puntero inteligente y mantener el recuento de referencias. Algo que los punteros intrusivos pueden hacer. – Rich

+0

Con punteros compartidos, no necesitará el tipo base en absoluto, lo que será un buen paso para desentrañar su código base. También obtendrá seguridad de hilo y seguridad de excepción. –

+0

El mayor problema con el puntero compartido es que tendré que usar adecuadamente el tipo compartido. También tengo que poder usar el puntero c básico, simplemente hay demasiado código para convertir. – Rich

4

Puede usar boost shared_ptr. Con esto puede proporcionar un eliminador personalizado (consulte this SO thread sobre cómo hacerlo). Y en ese eliminador personalizado sabes que has llegado al último recuento de referencias. Ahora puede eliminar el puntero de la caché.

+0

¿Se pueden agregar eliminadores personalizados a los punteros intrusivos? (Sé que puedo escribir mi propia versión de punteros intrusivos) – Rich

+1

Mientras el puntero esté almacenado en la caché, el recuento de referencias nunca caerá a 0 y no se invocará el eliminador personalizado. – visitor

+1

@visitor: Sí, tiene razón, pero el OP puede rediseñar el mecanismo de caché. Deje que el caché contenga punteros crudos que serán envueltos por 'shared_ptr' antes de que las ventanas de la GUI accedan a él. Cuando todos los punteros quedan fuera del alcance, el usuario puede eliminar la entrada de caché y también eliminarla si es necesario. – Abhay

1

Necesita mantener en su caché weak pointers en lugar de shared_ptr.

+0

Tenga en cuenta que un 'weak_ptr' no es un" puntero "en absoluto. No tiene semántica "puntero", sino semántica "referencia débil". – curiousguy

0

Podría considerar escribir un intrusive_weak_ptr para su clase de caché. De todos modos, necesitará hacer algo para limpiar los punteros débiles caducados de su caché, pero no es tan crítico como limpiar los objetos almacenados en la memoria caché.

http://lists.boost.org/boost-users/2008/08/39563.php es una implementación que se publicó en la lista de correo de impulso. No es seguro para subprocesos, pero podría funcionar para usted.

Cuestiones relacionadas