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örtMein
container_ptrist übrigens nur eine schlechte Kopie vonstd::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_ptrhat aber wiestd::auto_ptrMove-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.