2010-03-05 2 views
7

Ich habe versucht, intelligente Zeiger zu verwenden, um eine vorhandene App zu aktualisieren, und ich versuche, ein Rätsel zu überwinden. In meiner App habe ich einen Cache von Objekten, zum Beispiel können wir sie Bücher nennen. Nun wird dieser Büchercache nach ID angefordert und wenn sie im Cache sind, werden sie zurückgegeben, wenn das Objekt nicht von einem externen System angefordert wird (langsamer Vorgang) und dem Cache hinzugefügt wird. Sobald im Cache viele Fenster in der App geöffnet werden können, kann jedes dieser Fenster einen Verweis auf das Buch nehmen. In der vorherigen Version der App musste der Programmierer AddRef und Release beibehalten, wenn jedes Fenster, das das Book-Objekt verwendete, geschlossen wurde, würde das endgültige Release (auf dem Cache-Manager) das Objekt aus dem Cache entfernen und das Objekt löschen.Wie entferne ich Smart Pointer aus einem Cache, wenn keine Referenzen mehr vorhanden sind?

Vielleicht haben Sie hier die schwache Verbindung in der Kette entdeckt, natürlich erinnert sich der Programmierer daran, AddRef und Release aufzurufen. Jetzt bin ich zu Smartpointern (boost :: intrusiv) übergegangen. Ich muss mir keine Sorgen mehr machen über AddRef und Release. Dies führt jedoch zu einem Problem, da der Cache einen Verweis auf das Objekt hat. Wenn das letzte Fenster geschlossen wird, wird der Cache daher nicht benachrichtigt, dass kein anderer Benutzer eine Referenz hält.

Meine ersten Gedanken waren, den Cache regelmäßig zu durchlaufen und Objekte mit einer Referenzzahl von eins zu löschen. Mir gefiel diese Idee nicht, da es sich um eine Order-N-Operation handelte und ich mich nicht richtig fühlte. Ich habe ein Callback-System entwickelt, das besser, aber nicht fantastisch ist. Ich habe den Code für das Rückrufsystem eingefügt, aber ich fragte mich, ob jemand einen besseren Weg hatte, dies zu tun?

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 

Prost Reiche

+0

Sie sollten "BaseObject" nicht kopierbar machen (indem Sie einen privaten Kopierkonstruktor und einen Zuweisungsoperator deklarieren, aber nicht definieren), oder es in irgendeiner Weise korrekt kopierbar machen. Ebenso hat 'Book' eine gefährliche Kopiesemantik, die am besten mit' std :: string' anstelle eines manuell verwalteten Arrays behoben wird. –

+0

virtuelle dtor bitte !!! – curiousguy

Antwort

9

ich nie boost :: intrusive Smart-Pointer verwendet, aber wenn Sie Shared_ptr intelligente Zeiger verwenden würden, Sie weak_ptr Objekte für den Cache nutzen könnten.

Diese Zeiger weak_ptr zählen nicht als Referenz, wenn das System beschließt, ihren Speicher freizugeben, aber es kann verwendet werden, um einen shared_ptr abzurufen, solange das Objekt noch nicht gelöscht wurde.

+0

Ich muss intrusive Zeiger verwenden, da die Codebasis riesig und sehr miteinander verbunden ist. Ich kann den Basistyp nicht zu einem intelligenten Zeiger ändern. Ich muss auch in der Lage sein, einen vorhandenen Zeiger in einen intelligenten Zeiger zu ändern und den Verweiszähler zu behalten. Etwas, das aufdringliche Zeiger tun können. – Rich

+0

Mit freigegebenen Zeigern brauchen Sie den Basistyp überhaupt nicht, was ein guter Schritt zur Entschlüsselung Ihrer Codebasis ist. Außerdem erhalten Sie Thread-Sicherheit und Ausnahmesicherheit. –

+0

Das größte Problem mit geteilten Zeiger ist, dass ich die Verwendung des freigegebenen Typs ordnungsgemäß festlegen müsste. Ich muss in der Lage sein, den grundlegenden c-Zeiger auch zu verwenden, es gibt einfach zu viel Code, um zu konvertieren. – Rich

4

Sie können boost shared_ptr verwenden. Mit diesem können Sie einen benutzerdefinierten Deleter bereitstellen (siehe this SO Thread, wie es geht). Und in diesem benutzerdefinierten Löschprogramm wissen Sie, dass Sie die letzte Referenzanzahl erreicht haben. Sie können den Zeiger jetzt aus dem Cache entfernen.

+0

Können Sie den Intrusive-Zeigern benutzerdefinierte Deletes hinzufügen? (Ich weiß, ich kann, indem ich meine eigene Version von intrusiven Zeigern schreiben) – Rich

+1

Solange der Zeiger im Cache gespeichert ist, wird die Referenzanzahl nie auf 0 fallen und die benutzerdefinierte Deleter wird nicht aufgerufen. – visitor

+1

@visitor: Ja, Sie haben Recht, aber das OP kann den Cache-Mechanismus neu gestalten. Lassen Sie den Cache rohe Zeiger enthalten, die von 'shared_ptr' umschlossen werden, bevor auf die GUI-Fenster zugegriffen wird. Wenn alle Zeiger den Gültigkeitsbereich verlassen, kann der Benutzer den Cache-Eintrag entfernen und bei Bedarf auch löschen. – Abhay

1

Sie müssen in Ihrem Cache weak pointers anstelle von shared_ptr behalten.

+0

Bitte beachten Sie, dass ein "weak_ptr" kein "Zeiger" ist. Es hat nicht "Zeiger" semantische, sondern "schwache Referenz" semantisch. – curiousguy

0

Sie könnten überlegen, eine intrusive_weak_ptr für Ihre Cache-Klasse zu schreiben. Sie müssen immer noch etwas tun, um die abgelaufenen schwachen Zeiger in Ihrem Cache von Zeit zu Zeit zu bereinigen, aber es ist nicht so wichtig wie das Aufräumen der tatsächlich zwischengespeicherten Objekte.

http://lists.boost.org/boost-users/2008/08/39563.php ist eine Implementierung, die in der Boost-Mailingliste veröffentlicht wurde. Es ist nicht Thread-sicher, aber es könnte für Sie arbeiten.