Hilfsfunktion zum Löschen von STL-Containern



  • Hallo zusammen,

    ich habe mir schon vor einiger Zeit zwei Funktionen geschrieben die mir helfen STL-Container (list, vector) zu löschen:

    template <typename T>
    struct delete_unary : public std::unary_function <T, void>
    {
    void operator () (T &t)
    {
    delete t;
    }
    };

    template <typename T, typename S>
    void delete_stl_container (T &t)
    {
    for_each (t.begin (), t.end (), delete_unary <S> ());
    t.clear ();
    }

    Ich benutze es dann so:

    std::vector <State*> mStateList;
    ...
    delete_stl_container <std::vector <State*>, State*> (mStateList);

    Jetzt möchte ich es gerne für std::map benutzen...

    std::map <std::string, Part*> mPartList;
    ...
    delete_stl_container <std::map <std::string, Part*>, Part*> (mPartList);

    ...und bekomme diesen Kompilerfehler:

    In file included from /usr/include/c++/4.4/algorithm:62,
    from /home/andreas/src/svn/e/trunk/BINDINGS/cxx/eflxx/include/eflxx/MemoryUtil.h:5,
    from /home/andreas/src/svn/e/trunk/BINDINGS/cxx/eflxx/include/eflxx/Eflxx.h:9,
    from /home/andreas/src/svn/e/trunk/BINDINGS/cxx/evasxx/include/evasxx/Object.h:10,
    from ../include/edjexx/Part.h:10,
    from Object.cpp:5:
    /usr/include/c++/4.4/bits/stl_algo.h: In function ‘_Funct std::for_each(_IIter, _IIter, _Funct) [with _IIter = std::_Rb_tree_iterator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Edjexx::Part*> >, _Funct = Eflxx::delete_unaryEdjexx::Part*]’:
    /home/andreas/src/svn/e/trunk/BINDINGS/cxx/eflxx/include/eflxx/MemoryUtil.h:37: instantiated from ‘void Eflxx::delete_stl_container(T&) [with T = std::map<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Edjexx::Part*, std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Edjexx::Part*> > >, S = Edjexx::Part*]’
    Object.cpp:75: instantiated from here
    /usr/include/c++/4.4/bits/stl_algo.h:4200: error: no match for call to ‘(Eflxx::delete_unaryEdjexx::Part*) (std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Edjexx::Part*>&)’
    /home/andreas/src/svn/e/trunk/BINDINGS/cxx/eflxx/include/eflxx/MemoryUtil.h:28: note: candidates are: void Eflxx::delete_unary<T>::operator()(T&) [with T = Edjexx::Part*]

    Ich vermute schon warum es nicht geht. Ich will ja nur das "second" Argument "Part*" löschen und nicht das ganze iterator-Ergebnis.

    Auf Anhieb habe ich keine Idee wie ich meine Hilfsfunktion so abändern kann, dass es auch std::map unterstützt. Zur Not halt mit einer zweiten Hilfsfunktion.

    Hat jemand eine kreative Idee und kann mir helfen?

    Gruß
    Andreas



  • template <typename T>
    struct delete_second
    {
       void operator() (T& object) const
       {
          delete object.second;
       }
    };
    
    std::map<int, int*> m;
    m[3] = new int(27));
    std::for_each(m.begin(), m.end(), delete_second<std::map<int, int*>::value_type>());
    


  • delete_stl_container <std::vector <State*>, State*> (mStateList); // deine Funktion
    std::for_each(map.begin(), map.end(), delete_second<std::map<int, int*>::value_type>()); // thetas komische `Lösung'
    delete_stl_container_plusplus(vec); // meine schöne und einheitliche
    delete_stl_container_plusplus(map); // Schnittstelle
    ...
    
    template <typename T>
    void delete_stl_container_plusplus (T &cnt)
    {
      for (typename T::iterator it=cnt.begin(); it!=cnt.end(); ++it)
        delete *it;
      cnt.clear();
    }
    
    // Spezialisation, eigentlich eine Überladung, die für alle std::maps gilt
    template <typename Key, typename T>
    void delete_stl_container_plusplus (std::map<Key, T> &cnt)
    {
      for (typename std::map<Key, T>::iterator it=cnt.begin(); it!=cnt.end(); ++it)
        delete it->second;
      cnt.clear ();
    }
    


  • Danke euch beiden!

    Gruß
    Andreas



  • Wer ι (Iota) sagt, muss auch κ (Kappa) sagen (bin der gleiche Unreg wie Iota).

    template <typename T>
    class container_ptr {
    public:
      container_ptr (T *ptr=0) : ptr(ptr) {}
     ~container_ptr () { delete ptr; }
      operator T* ()   { return ptr; }
      void reset  (T *p=0) { if (p!=ptr) { delete ptr; ptr = p; } }
      T *  release(T *p)   { T *tmp = ptr; ptr = 0; return tmp;   }
      container_ptr& operator=(container_ptr& a) { reset(a.release()); return *this; }
      container_ptr& operator=(T *a            ) { reset(a          ); return *this; }
    private:
      T *ptr;
    };
    
    std::map <int, container_ptr<State> > mappi;
    for (int i=0;i<256;++i)
      mappi[i] = new State; // alles geht normal
    mappi.clear(); // alle Elemente werden zerstört
    

    Mein container_ptr ist übrigens nur eine schlechte Kopie von std::auto_ptr .
    Falls du Boost benutzt, kannst du dir noch die Pointer Container anschauen, die machen das ganze ohne einen zusätzlichen Wrapper.



  • In STL-Containern darfst du nur Objekte mit Wertsemantik speichern. Dein container_ptr hat aber wie std::auto_ptr Move-Semantik, da er das Originalobjekt bei einer Kopie verändert. Das kann zu unangenehmen Überraschungen führen...

    Für mehr Informationen kannst du mal bei Google "auto_ptr in stl container" eingeben.





  • Kappa schrieb:

    Wer ι (Iota) sagt, muss auch κ (Kappa) sagen (bin der gleiche Unreg wie Iota).

    template <typename T>
    class container_ptr {
    public:
      container_ptr (T *ptr=0) : ptr(ptr) {}
     ~container_ptr () { delete ptr; }
      operator T* ()   { return ptr; }
      void reset  (T *p=0) { if (p!=ptr) { delete ptr; ptr = p; } }
      T *  release(T *p)   { T *tmp = ptr; ptr = 0; return tmp;   }
      container_ptr& operator=(container_ptr& a) { reset(a.release()); return *this; }
      container_ptr& operator=(T *a            ) { reset(a          ); return *this; }
    private:
      T *ptr;
    };
    

    Sorry, aber das ist totaler Blödsinn. Implizite Konvertierung von T* zu container_ptr<T> gehört sich nicht. Dein reset ist fehlerhaft. Der Compiler-generierte Kopierkonstruktor führt zu doppelten Löschungen.

    Dieser Ansatz funktioniert erst, wenn wir Rvalue-Referenzen haben und Container, die move-only Typen unterstützen. Und wenn es soweit ist, können wir dann std::unique_ptr<T> benutzen.


Anmelden zum Antworten