Container mit Zeigern und STL-Funktionstemplates



  • Ich habe einen Container mit Zeigern auf Objekte angelegt. Nun möchte ich für den Container die STL-Funktion "for_each" nutzen.

    class Object {
        void doSomething() {
    
        }
    };
    
    void main() {
        std::list<Object*> list;
    
        std::for_each(list.begin(), list.end(), doSomething) 
    }
    

    Dabei tritt das Problem auf, dass ich den angegebenen Code nicht nutzen kann, da der Iterator in for_each noch dereferenziert werden muss.

    Wie muss ich den Code umschreiben, damit er ausgeführt werden kann?



  • class Functor {
    void Functor(Object* p) {

    }
    };

    std::for_each(list.begin(), list.end(), Functor)



  • Danke, ich wollte aber eigentlich auf folgenden allgemeinen Fall hinaus, dass auch Argumente übergeben können ( Stroustrup, S.555-556):

    class Object {
        void doSomething() {
    
        }
    };
    
    void for_all_doSomething( std::list<Object*>& list ) {
        std::for_each(list.begin(), list.end(), mem_fun(&Object::doSomething))
    }
    
    void main() {
        std::list<Object*> list;    
        for_all_doSomething( );
    }
    

    Bei mir läuft dieser Code aber nicht.



  • for_all_doSomething( );
    

    Du musst hier list als Parameter übergeben, sonst kann es nicht gehen! Wenn es das nicht ist, dann poste mal ein ausführbares Minimalbeispiel und die Fehlermeldung!



  • OK, ich habe es mit folgendem Code ausprobiert, funktioniert aber nicht:

    object.h

    #if !defined (__OBJECT_H__)
    #define __OBJECT_H__
    
    #include <iostream>
    
    class Object {
    	static int s_iIndex;
    	int m_iIndex;
    
    public:
    	Object() : m_iIndex (s_iIndex++) { }
    	virtual ~Object() { }
    
    	void print()  const {
    		std::cout << m_iIndex << std::endl;
    	}
    };
    
    class ObjectIterator {
    
    public:
    	void objectIterator (const Object* o ) {
    		o->print();
    	}
    };
    
    int	Object::s_iIndex = 0;
    
    #endif
    

    main.cpp

    #include "Object.h"
    
    #include <list>
    #include <algorithm>
    
    void main() {
    
    	std::list<Object*> list;
    
    	for( int i=0; i<10; ++i )
    		list.push_back( new Object() );
    
    	std::for_each( list.begin(), list.end(), ObjectIterator::objectIterator );
    }
    

    Als Fehlermeldung kommt:

    main.cpp
    \microsoft visual studio\vc98\include\algorithm(37) :
    error C2064: Ausdruck ergibt keine Funktion

    \For_Each_Test\src\main.cpp(14) : Siehe Verweis auf Instantiierung der kompilierten Funktionsvorlage 'void (__thiscall ObjectIterator::*__cdecl std::for_each(class std::list<class Object *,class
    std::allocator<class Object *> >::iterator,class std::list<class Object *,class std::allocator<class Object > >::iterator,void (__thiscall ObjectIterator::)(const class Object *)))(const class Object *)'



  • 1.die funktion muss statisch sein, da sie sonst über ein objekt der klasse ObjectIterator aufgerufen werden muss.
    2.

    #if !defined (__OBJECT_H__)
    #define __OBJECT_H__
    

    ist ganz schlechter stil, der underscore mit nachfolgendem großbuchstaben, bzw der double underscore ist dem compiler und der stl vorbehalten.

    besser so:

    #ifndef object_h
    #define object_h
    
    void main()
    

    das ist vorsintflutlich!
    besser:

    int main()
    


  • Luca76 schrieb:

    OK, ich habe es mit folgendem Code ausprobiert, funktioniert aber nicht:
    [...]

    std::for_each( list.begin(), list.end(), ObjectIterator::objectIterator );
    }
    

    Der Aufruf ergibt keinerlei sinn. ObjectIterator::objectIterator ist kein gültiges C++. Ich schätze mal du willst hier die Adresse der Memberfunktion objectIterator bilden. Dafür fehlt der unäre Operator &

    std::for_each( list.begin(), list.end(), &ObjectIterator::objectIterator );
    

    Das wiederum ergibt in diesem Kontext keinen Sinn. Ein Methodenzeiger ist alleine nicht aufrufbar. for_each versucht aber für jedes Element der angegebenen Sequenz das über den dritten Parameter gegebene Funktionsobjekt aufzurufen.
    Ich weiß ehrlich gesagt nicht was genau du machen willst, aber wenn du ObjectIterator als Funktionsobjekt einsetzen willst, dann geht das so:

    class ObjectIterator { 
    
    public: 
        void operator() (const Object* o ) const { 
            o->print(); 
        } 
    };
    

    Der Aufruf sieht dann so aus:

    std::for_each( list.begin(), list.end(), ObjectIterator() );
    

    Wieso aber dann nicht gleich die print-Methode von Object benutzen?

    #include <list> 
    #include <algorithm> 
    #include <functional>
    
    int main() { 
    
        ...
        std::for_each( list.begin(), list.end(), std::mem_fun(&Object::print) ); 
    }
    


  • std::mem_fun(&Object::print)

    Wie funktioniert denn das? Woher kriegt std::mem_fun denn den Zeiger auf die Klasse?



  • Sorry. Meinte den Zeiger auf das Objekt... 😕



  • Neulings schrieb:

    std::mem_fun(&Object::print)

    Wie funktioniert denn das? Woher kriegt std::mem_fun denn den Zeiger auf die Klasse?

    mem_fun wandelt einen Aufruf der Form f(param) in die Form param->f().
    Die Objekte sind in diesem Fall also nacheinander die Elemente der Liste.



  • Danke für die Hinweise.

    HumeSikkins schrieb:

    #include <list> 
    #include <algorithm> 
    #include <functional>
    
    int main() { 
    
        ...
        std::for_each( list.begin(), list.end(), std::mem_fun(&Object::print) ); 
    }
    

    Ich habe es jetzt so probiert, nur leider kommt jetzt der Fehler:

    functional(233) : error C2562: '()' : 'void'-Funktion gibt einen Wert zurueck
    functional(232) : Siehe Deklaration von '()'
    functional(233) : Bei der Kompilierung der Member-Funktion 'void __thiscall std::mem_fun_t<void,class Object>::operator ()(class Object 😉 const' der Klassenvorlage

    Was ist jetzt noch falsch?



  • Luca76 schrieb:

    Was ist jetzt noch falsch?

    Dein Compiler. Der ist leider nicht ganz richtig in der Birne und in diesem Punkt meilenweit vom Standard entfernt.

    In diesem Fall bleiben dir nur zwei Möglichkeiten:
    1. Du gehst den Umweg über ein zusätzliches Funktionsobjekt (siehe Beispiel)
    2. Du spendierst deiner print-Methode einen Rückgabewert und machsts sie non-const.



  • Visual C++ 6 habe ich. 😉



  • Dein Compiler. Der ist leider nicht ganz richtig in der Birne und in diesem Punkt meilenweit vom Standard entfernt.

    😃 😃 😃
    manno bin ich froooooooooo
    dass ich

    Visual C++ 6

    nicht mehr hab

    😃 😃 😃



  • Luca76 schrieb:

    Visual C++ 6 habe ich. 😉

    man erkennt die qualität von C++ büchern daran, ob auf der cd ne autoren version von Vc6 ist^^



  • Luca76 schrieb:

    Visual C++ 6 habe ich. 😉

    Wenn's dich beruhigt, ich benutze ebenfalls hauptsächlich diesen Compiler 😉
    Was du als drittes machen könntest, wäre auf eine andere STL-Implementation umzusteigen. STLPort ist z.B. ganz schön.
    Als Übergangslösung würde ich auf jeden Fall die Variante mit dem zusätzlichen Funktionsobjekt verwenden.



  • Danke! 😉
    Habe STLport heruntergeladen, installiert und es funktioniert jetzt auch.



  • Wenn's dich beruhigt, ich benutze ebenfalls hauptsächlich diesen Compiler

    Darf man fragen wieso? Verwendet ihr den bei euch an der Uni?



  • Helium schrieb:

    Wenn's dich beruhigt, ich benutze ebenfalls hauptsächlich diesen Compiler

    Darf man fragen wieso? Verwendet ihr den bei euch an der Uni?

    Ja und nein. In unserem Informatikbereich wird hauptsächlich SunOS und Linux eingesetzt, demzufolge wird dort der gcc verwendet. Am HPI (Softwaresystemtechnik) wird hingegen eigentlich immer das neuste von Microsoft benutzt, sprich derzeit hauptsächlich Visual Studio .Net.

    Das ich den VC 6.0 benutze hat drei entscheidene Gründe:
    1. Ich bin Windows-Benutzer (komme mit Linux nach wie vor nicht zurecht).
    2. Ich habe nach wie vor Windows98 installiert, kann also die Ganze .NET-Geschichten nicht so ohne weiteres einsetzen.
    3. Die IDE des VC 6.0 (ergänzt um einige Plugins) ist meiner Meinung nach nach wie vor herausragend. Unter Berücksichtigung von 1. und 2. habe ich bisher nichts vergleichbares gesehen.

    Ich habe natürlich auch andere Compiler auf dem Rechner installiert (Win-Version des gcc, Cygwin + gcc sowie auch ein Linux mit immer aktuellem gcc), so dass ich auch mal problemlos mit Templates rumspielen kann. Für die normale Arbeit verwende ich aber immer den VC 6.0



  • So, ich wollte es jetzt auch mal so ausprobieren, dass ein Parameter übergeben werden kann.

    class Object {
    public:
        void print( const std::string& msg ) {
            std::cout << msg;
        }
    };
    
    int main( int argc, char *argv[] ) {
    
        std::list<Object*> objectList;
        std::string msg( "Ausgabe" );
    
        std::for_each( objectList.begin(), objectList.end(), std::bind2nd( std::mem_fun(&Object::print), msg ) );
        return 0;
    }
    

    Beim Compilieren kommt dann der Fehler:
    \stlport-4.6.2\stlport\stl\_function.h(166) : warning C4181: Auf Referenztyp angewendeter Qualifizierer wird ignoriert
    \Test_Project\src\main.cpp(52) : Siehe Verweis auf Instantiierung der kompilierten Klassenvorlage '_STL::binder2nd<class _STL::mem_fun1_t<void,class Object,class _STL::basic_string<char,s
    truct std::char_traits<char>,class _STL::allocator<char> > const &> >'
    \stlport-4.6.2\stlport\stl\_function.h(165) : error C2529: '<unbekannt>' : Verweis auf Verweis ungueltig
    \Test_Project\src\main.cpp(52) : Siehe Verweis auf Instantiierung der kompilierten Klassenvorlage '_STL::binder2nd<class _STL::mem_fun1_t<void,class Object,class _STL::basic_string<char,s
    truct std::char_traits<char>,class _STL::allocator<char> > const &> >'

    Kann man eigentlich auch 2 Parameter übergeben?


Anmelden zum Antworten