2010-03-05 11 views
7

J'ai essayé d'utiliser des pointeurs intelligents pour mettre à jour une application existante, et j'essaie de surmonter un casse-tête. Dans mon application, j'ai un cache d'objets, par exemple, appelons les livres. Maintenant, ce cache de livres est demandé par ID et s'ils sont dans le cache, ils sont retournés, sinon l'objet est demandé à partir d'un système externe (opération lente) et ajouté au cache. Une fois dans le cache de nombreuses fenêtres peuvent être ouvertes dans l'application, chacune de ces fenêtres peut prendre une référence au livre. Dans la version précédente de l'application, le programmeur devait maintenir AddRef et Release, lorsque chaque fenêtre utilisant l'objet Book était fermée, la version finale (sur le gestionnaire de cache) supprimait l'objet du cache et supprimait l'objet.Comment supprimer des pointeurs intelligents d'un cache lorsqu'il n'y a plus de références?

Vous avez peut-être repéré le maillon faible de la chaîne ici, c'est bien sûr le programmeur qui se souvient d'appeler AddRef et Release. Maintenant, je suis passé aux pointeurs intelligents (boost :: intrusive) Je n'ai plus à m'inquiéter d'appeler AddRef et Release. Cependant, cela conduit à un problème, le cache a une référence à l'objet, donc quand la fenêtre finale est fermée, le cache n'est pas averti que personne d'autre ne détient une référence. Mes premiers réflexions ont été de parcourir périodiquement le cache et de purger les objets avec un nombre de références égal à 1. Je n'ai pas aimé cette idée, car c'était une opération de l'Ordre N et je ne me sentais pas bien. J'ai mis au point un système de rappel, ce qui est mieux mais pas fantastique. J'ai inclus le code pour le système de rappel, mais je me demandais si quelqu'un avait une meilleure façon de le faire?

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 

Vive Rich

+0

Vous devriez rendre 'BaseObject' uncopyable (en déclarant, mais ne définissant pas, un constructeur de copie privée et un opérateur d'affectation), ou le rendre correctement copiable d'une certaine façon. De même, 'Book' a une sémantique de copie dangereuse, qui est mieux fixée en utilisant' std :: string' à la place d'un tableau géré manuellement. –

+0

dtor virtuel s'il vous plaît !!! – curiousguy

Répondre

9

Je ne ai jamais utilisé boost :: pointeurs intelligents intrusive, mais si vous voulez utiliser des pointeurs shared_ptr intelligents, vous pouvez utiliser weak_ptr objets pour votre cache. Ces pointeurs weak_ptr ne comptent pas comme référence lorsque le système décide de libérer de la mémoire, mais peuvent être utilisés pour récupérer un shared_ptr tant que l'objet n'a pas encore été supprimé.

+0

Je dois utiliser des pointeurs intrusifs car la base de code est énorme et très interconnectée. Je ne peux pas changer le type de base en un pointeur intelligent. Je dois également pouvoir changer un pointeur existant en pointeur intelligent et maintenir le compte de référence. Quelque chose que les pointeurs intrusifs peuvent faire. – Rich

+0

Avec les pointeurs partagés, vous n'aurez pas du tout besoin du type de base, ce qui sera une bonne étape pour démêler votre base de code. Vous bénéficierez également de la sécurité des threads et de la sécurité des exceptions. –

+0

Le plus gros problème avec le pointeur partagé est que je devrais propergate l'utilisation du type partagé. Je dois aussi pouvoir utiliser le pointeur c de base, il y a tout simplement trop de code à convertir. – Rich

4

Vous pouvez utiliser boost shared_ptr. Avec cela, vous pouvez fournir un suppresseur personnalisé (voir this SO thread sur la façon de le faire). Et dans cet effaceur personnalisé, vous savez que vous avez atteint le dernier décompte des références. Vous pouvez maintenant supprimer le pointeur du cache.

+0

Pouvez-vous ajouter des suppresseurs personnalisés aux pointeurs intrusifs? (Je sais que je peux en écrivant ma propre version de pointeurs intrusifs) – Rich

+1

Tant que le pointeur est stocké dans le cache, le compte de référence ne tombera jamais à 0 et le suppresseur personnalisé ne sera pas appelé. – visitor

+1

@visitor: Oui, vous avez raison, mais l'OP peut re-concevoir le mécanisme de cache. Laissez le cache contenir des pointeurs bruts qui seront enveloppés par 'shared_ptr' avant d'être accédé par les fenêtres de l'interface graphique. Lorsque tous les pointeurs sortent de la portée, l'utilisateur peut supprimer l'entrée de cache et également supprimer le même si nécessaire. – Abhay

1

Vous devez conserver dans votre cache weak pointers au lieu de shared_ptr.

+0

Veuillez noter qu'un 'weak_ptr' n'est pas du tout un" pointeur ". Il n'a pas de sémantique "pointeur", mais sémantique de "référence faible". – curiousguy

0

Vous pourriez envisager d'écrire un intrusive_weak_ptr pour votre classe de cache. Vous aurez toujours besoin de faire quelque chose pour nettoyer les pointeurs faibles expirés dans votre cache de temps en temps, mais ce n'est pas aussi important que de nettoyer les objets en cache réels.

http://lists.boost.org/boost-users/2008/08/39563.php est une implémentation qui a été publiée dans la liste de diffusion boost. Ce n'est pas sûr pour les threads, mais cela pourrait fonctionner pour vous.