Zeiger auf 0 in map<Ty1, Ty2 *> mit transform



  • Hallo,

    in dem Buch von Bjarne Stroustrup "The C++ Programming Language 3rd Ed"
    gibt es ein Beispiel, einen Zeiger auf 0 zu setzen mit transform.

    template <class T> T * delete_ptr(T * p) { delete p; return 0; }
    
    void purge(deque <Shape *> & s)
    {
    	transform (s.begin(), s.end(), s.begin(), delete_ptr );
    / / ...
    }
    

    Ich wollte eigentlich genau das gleiche für eine map erreichen,
    die ich in eine Klasse CListTalent einbette.

    typedef const pair<string, CTalentADT *> CONST_TALENT_ELEMENT;
    
    CONST_TALENT_ELEMENT CTransformTalentDeletePointer::operator() (const CONST_TALENT_ELEMENT & talent) const
    {
    	delete talent.second;
    	return CONST_TALENT_ELEMENT("", 0);
    
    }
    
    CListTalent::~CListTalent()
    {
    	transform( m_list.begin(), m_list.end(), m_list.begin(), CTransformTalentDeletePointer() );
    
    } // default destructor
    

    Allerdings spuckt der Compiler eine Fehlermeldung die ich auch beim zweiten Hinschauen
    nicht verstehe.

    1>listtalent.cpp
    1>C:\Program Files\Microsoft Visual Studio 9.0\VC\include\algorithm(671) : error C2582: Die Funktion 'operator =' ist in 'std::pair<_Ty1,_Ty2>' nicht verfügbar
    1> with
    1> [
    1> _Ty1=const std::string,
    1> _Ty2=CTalentADT *
    1> ]
    1> C:\Program Files\Microsoft Visual Studio 9.0\VC\include\algorithm(696): Siehe Verweis auf die Instanziierung der gerade kompilierten Funktions-template "_OutIt std::_Transform<std::_Tree<_Traits>::iterator,_OutIt,_Fn1,std::_Iter_random_helper<_Cat1,_Cat2>::_Iter_random_cat>(_InIt,_InIt,_OutIt,_Fn1,_InOutItCat,std::_Range_checked_iterator_tag)".
    1> with
    1> [
    1> _OutIt=std::_Tree<std::_Tmap_traits<std::string,CTalentADT *,CGermanLexicographicalCompare,std::allocator<std::pair<const std::string,CTalentADT *>>,false>>::iterator,
    1> _Traits=std::_Tmap_traits<std::string,CTalentADT *,CGermanLexicographicalCompare,std::allocator<std::pair<const std::string,CTalentADT *>>,false>,
    1> _Fn1=CTransformTalentDeletePointer,
    1> _Cat1=std::_Tree<std::_Tmap_traits<std::string,CTalentADT *,CGermanLexicographicalCompare,std::allocator<std::pair<const std::string,CTalentADT *>>,false>>::iterator::iterator_category,
    1> _Cat2=std::_Tree<std::_Tmap_traits<std::string,CTalentADT *,CGermanLexicographicalCompare,std::allocator<std::pair<const std::string,CTalentADT *>>,false>>::iterator::iterator_category,
    1> _InIt=std::_Tree<std::_Tmap_traits<std::string,CTalentADT *,CGermanLexicographicalCompare,std::allocator<std::pair<const std::string,CTalentADT *>>,false>>::iterator,
    1> _InOutItCat=std::_Iter_random_helper<std::_Tree<std::_Tmap_traits<std::string,CTalentADT *,CGermanLexicographicalCompare,std::allocator<std::pair<const std::string,CTalentADT *>>,false>>::iterator::iterator_category,std::_Tree<std::_Tmap_traits<std::string,CTalentADT *,CGermanLexicographicalCompare,std::allocator<std::pair<const std::string,CTalentADT *>>,false>>::iterator::iterator_category>::_Iter_random_cat
    1> ]
    1> .\listtalent.cpp(53): Siehe Verweis auf die Instanziierung der gerade kompilierten Funktions-template "std::_Tree<_Traits>::iterator std::transform<std::_Tree<_Traits>::iterator,std::_Tree<_Traits>::iterator,CTransformTalentDeletePointer>(_InIt,_InIt,_OutIt,_Fn1)".
    1> with
    1> [
    1> _Traits=std::_Tmap_traits<std::string,CTalentADT *,CGermanLexicographicalCompare,std::allocator<std::pair<const std::string,CTalentADT *>>,false>,
    1> _InIt=std::_Tree<std::_Tmap_traits<std::string,CTalentADT *,CGermanLexicographicalCompare,std::allocator<std::pair<const std::string,CTalentADT *>>,false>>::iterator,
    1> _OutIt=std::_Tree<std::_Tmap_traits<std::string,CTalentADT *,CGermanLexicographicalCompare,std::allocator<std::pair<const std::string,CTalentADT *>>,false>>::iterator,
    1> _Fn1=CTransformTalentDeletePointer
    1> ]

    Es hat anscheinend was mit den Iteratoren zu tun, aber kann mir jemand da auf die Sprünge helfen,
    wie ich das Problem beheben kann?

    Vielen Dank
    Rahul


  • Mod

    Die Iteratoren in eine Map sind nicht mutable, weil der erste Teil des Wertpaares konstant ist. Mithin können solche Iteratoren nicht für die Ausgabe von transform - welches das Ergebnis der Transformationsfunktion entsprechend zuweist - sein. Man könnte diese Iteratoren adaptieren, das dürfte allerdings zuviel Aufwand bedeuten. Eine andere Möglichkeit stellen Smartpointer dar.



  • Was spricht gegen sowas? std::for_each() scheint mir hier sowieso geeigneter als std::transform() .

    void delete_and_reset(std::pair<const int, std::string*>& pair)
    {
    	delete pair.second;
    	pair.second = 0;
    }
    
    int main()
    {
    	std::map<int, std::string*> map;
    	map[3] = new std::string("drei");
    	map[4] = new std::string("vier");
    	map[7] = new std::string("sieben");
    
    	std::for_each(map.begin(), map.end(), &delete_and_reset);
    }
    

    Allerdings sind rohe, besitzende Zeiger in STL-Containern generell etwas heikel. Einmal einen Container kopiert, und man hat relativ schnell undefiniertes Verhalten. Auch die Gefahr von Memory Leaks sollte nicht unterschätzt werden. Ich persönlich mag es auch nicht, wenn ich mich wieder manuell um den Speicherverwaltung kümmern muss, zumal das Konzept der Container unter anderem darin besteht, mir genau diese Arbeit abzunehmen.

    Wie bereits erwähnt stellen Smart-Pointer eine Alternative dar. Hier sollte man sich jedoch im Klaren darüber sein, dass z.B. boost::shared_ptr einen kleinen Overhead durch die Referenzzählung mit sich bringt, von welchem man hier nicht wirklich profitiert, da kaum Shared-Ownership benötigt wird, sondern die meiste Zeit ein einzelner Container die Verwaltung übernimmt. Eine andere Möglichkeit wäre Boost.PointerContainer, wobei ich boost::ptr_map etwas gewöhnungsbedürftig finde.



  • Danke für die fixen Antworten...

    Ich wollte eigentlich es mit transform machen, weil
    es so eine Art Konsistenz reinbringen wollte, der Lesbarkeit wegen.
    Also immer wenn dem Zeiger ein neuer Wert zugewiesen wird,
    mit transform zu hantieren, z.B im Kopierkonstruktor und Destruktor,
    ansonsten mit for_each.

    Im Kopierkonstruktor hat das auch funktioniert, weil ich nicht
    den Iterator auf sich selbst zurückliefere.

    Da ich mich noch nicht mit SmartPointern so gut auskenne,
    werde ich nun zu for_each zurückkehren, auch wenn das für
    das Funktionsobjekt des Kopierkonstruktors ein bisserl weniger schön ist.

    Habt ihr noch tolle Referenzen für SmartPointer,
    die ihr für sehr gut zum Lernen haltet.
    Ich muss mich dann erst einlesen,
    um abzuwägen.

    Alles Gute
    Rahul



  • Rahul0891 schrieb:

    Ich wollte eigentlich es mit transform machen, weil
    es so eine Art Konsistenz reinbringen wollte, der Lesbarkeit wegen.

    Der Lesbarkeit wegen würde ich gerade std::for_each() verwenden. Sonst gibst du dauernd einen überflüssigen Parameter an. std::transform() wird üblicherweise dazu verwendet, eine Funktion auf eine Iterator-Range anzuwenden und die Resultate in einer neuen Range zu speichern.

    Rahul0891 schrieb:

    Da ich mich noch nicht mit SmartPointern so gut auskenne,
    werde ich nun zu for_each zurückkehren, auch wenn das für
    das Funktionsobjekt des Kopierkonstruktors ein bisserl weniger schön ist.

    Was ist weniger schön?

    Rahul0891 schrieb:

    Habt ihr noch tolle Referenzen für SmartPointer,
    die ihr für sehr gut zum Lernen haltet.
    Ich muss mich dann erst einlesen,
    um abzuwägen.

    Im Magazin findest du einen Artikel dazu. Da gibts auch sonst viele lesenswerte Artikel, zum Beispiel über Templates, Operatorüberladung, STL oder Zeiger.

    P.S.: Du brauchst keine manuellen Zeilenumbrüche zu machen, Browser können das automatisch. Ich finde es eher hinderlich beim Lesen, wenn man dauernd auf die neue Zeile springen muss. 😉


Anmelden zum Antworten