Intelligente Pointer-Liste?



  • Hi,

    ich hatte bisher folgende Liste in einer Klasse "Base":

    class Base
    {
     class BaseEntry {
      // ...
     };
     std::list<BaseEntry*> EntryPtrList;
     virtual void add_entry_to_list() = 0;
    };
    

    von der Oberklasse haben verschiedene Klassen abgeleitet, z.B. Inherited1:

    class Inherited1 : public Base
    {
     class InheritedEntry : public Base::BaseEntry {
      // ..
     };
     void add_entry_to_list() {
      EntryPtrList.push_back( new InheritedEntry );
     }
    };
    

    Das Problem ist ein wenig kompliziert. Da könnten jetzt Syntax-Error drin sein aber ich denke das Prinzip ist verständlich.

    Der Nachteil ist, dass jede vererbte Klasse einen anderen Entry hat und von daher jedes mal die add_entry_to_lsit()-Funktion neu implementiert werden müsste (was schon ein bischen lästig wird...).

    Ich würde dieses add gerne generell in die add-Funktion der Base-Klasse verlegen um diese Arbeit unnötig zu machen. Wie das geht weiß ich nicht, aber ich dachte mir, vllt könnte ich der Liste bei ihrer Initialisierung sagen, welche Pointer-Arten sie allokieren soll (Also, dass sie statt Base* einfach Inherited* allokiert). Jede Liste hat einen "allocator type" (meist das zweite Template), vllt ist es das? (Was heißt das überhaupt?)

    Wäre es so auch möglich, dass ich statt einer list<BaseEntry*> eine list<BaseEntry> nehmen könnte? Denn die Liste allokiert ja sowieso schon Speicher für das einzufügende Element, und dass ich dann mit new nochmal allokiere ist ja "doppelt gemoppelt" oO - sprich ich will nicht den Pointer an die Liste hängen sondern die Entry-Klasse - aber immer noch virtuell.

    Gruß,
    voipointer



  • Was soll das werden? Warum fügt add_entry_to_list einfach irgend ein neues Element ein? Warum übergibst du das nicht als Parameter?



  • So wie mein Vorposter schon geschrieben hat, ist es das Beste einfach die Methode

    void add(Base *base)
    {
      EntryPtrList.push_back( base );
    }
    

    zu verwenden.
    Du mußt zwar dann die Erstellung des Objektes außerhalb vornehmen, aber was ist, wenn du Konstruktoren mit Parametern verwenden willst - das könntest du mit deiner Version nicht machen (da du die Signatur der Methode add ja nicht beim Überschreiben ändern darfst).

    Alternativ könntest du auch eine Template-Methode verwenden:

    template <typename TBase>
    void add()
    {
      EntryPtrList.push_back( new TBase() );
    }
    
    // Aufruf dann mit:
    base.add<Inherited>();
    

    Und zu deinen ganzen Fragen:

    - der Allokator ist für die interne Speicherbeschaffung zuständig, d.h. dieser hat nichts mit deinem Problem zu tun - ändere diesen also erstmal nicht!

    - Und die Liste mußt du mit Pointern füllen, da sonst der virtuelle Zugriff (Polymorphie) nicht klappt, denn bei std::list<BaseEntry> hätte jedes Element nur sizeof(BaseEntry) als Größe. Da abgeleitete Klassen meistens noch zusätzliche Membervariablen besitzen (insbesondere, wenn Base pure virtual ist), könnte in der Liste nur der Basisanteil gespeichert werden.

    Du mußt außerdem bedenken, daß du beim Löschen eines Elementes aus der Liste von Hand das Element wieder löschen mußt (mittels delete).
    Eine Alternative dazu sind die Shared-Pointer (z.B. boost::shared_ptr).


Anmelden zum Antworten