Vector aus Objekten -> auf Objekte über den Iterator zugreifen und verändern



  • Guten Morgen allerseits 🙂

    Ich komme hier an einer Stelle nicht weiter mit meinem Programm, also stell ich es zur Debatte 🙂

    In einem automatisierten Durchlauf über einen File, erzeuge ich aus den Infos in den Zeilen Objekte verschiedener Klassen und hänge sie an meinenm Elemente-Vector dran. Dann will ich in einem weiteren Schritt ein bestimmtes Element im Vector finden und zusätzliche Attribute setzen:

    ElemVec.push_back(new ElemKreis(string Label_, string Farbe_));
    . . .
    std::vector<ElemVec>::const_iterator itElem = std::find_if(ElemVec.begin(), ElemVec.end(), FindObj(Label_));
    std::cout << itElem -> Farbe << endl;
    // z. B. Blue
    (*itElem).Farbe = "Red";
    std::cout << itElem -> Farbe << endl;
    // Funktioniert nicht
    

    Mache ich n Denkfehler? Ich will an dem Elementen, auf dem itElem zeigt etwas verändern, für mich sieht (*itElem).Farbe = "Red" in bester Ordnung aus 🙂

    Viele Grüße,
    C.



  • 1. "Funktioniert nicht" ist keine gute Beschreibung.
    2. Das const bei const_iterator sollte dir zu denken geben 😉 => raus damit.



  • Dein Vektor beheimatet doch Zeiger auf Objekte?!
    Dementsprechend mußt Du aus

    (*itElem).Farbe
    
    (*itElem)->Farbe
    

    machen.
    Trotzdem mußt Du natürlich einen nichtkonstanten Iterator nehmen.



  • Und der Preis geht an .. Michael E.! 🙂
    Das war wirklich das const bei dem const_iterator.

    Sonst sind in dem Vector Objekte, keine Zeiger - (*itElem).Farbe war schon richtig 🙂

    3246725 Dank 🙂



  • Cordula schrieb:

    Sonst sind in dem Vector Objekte, keine Zeiger

    Dann hab ich das hier:

    ElemVec.push_back(new ElemKreis(string Label_, string Farbe_));
    

    nicht verstanden ...



  • Cordula schrieb:

    Sonst sind in dem Vector Objekte, keine Zeiger - (*itElem).Farbe war schon richtig 🙂

    In einem nicht leeren std::vector<> sind immer Objekte -- auch wenn es Zeiger-Objekte sind. Wenn (*itElem).Farbe richtig sein soll, dann hast Du in Deinem ersten Beitrag gelogen.



  • Beides ist richtig!

    Entweder dereferenzieren und Punktoperator:

    (*itElem).Farbe
    

    oder Zeigeroperator:

    itElem->Farbe
    


  • Ähh ... ne.

    (*itElem)
    

    gibt doch das gespeicherte Element zurück(itElem ist ja 'nur' der Iterator).
    Das gespeichert Element sollte aber nach

    ElemVec.push_back(new ElemKreis(string Label_, string Farbe_));
    

    ein Zeiger sein?!



  • Und ElemVec ist darüber hinaus auch noch ein Typ 😉



  • Ähm .. liebe Leute, ihr verwandelt das Wenige was ich (denke dass ich) weiß in einem unüberschaubaren explosiven Wirrwarr mit unerklärlichen verknotteten Zusammenhängen.

    Also, nochmal, zu meinem Verständnis:

    Ich habe die Klasse ElemGeomForm mit Unterklassen ElemKreis und ElemRechteck.
    Ich erzeuge Objekte der Klassen ElemKreis und ElemRechteck mittels einem erweiterten Konstruktor, z. B. von verschiedenen Farben:

    ElemKreis::ElemKreis(string Label_, string Farbe_):Label(Label_), Farbe(Farbe_){}
    

    Diese Objekte packe ich in einem ElemVec, der definiert ist als:

    vector<ElemGeomForm> ElemVec;
    ElemVec.push_back(new ElemKreis(string Label_, string Farbe_));
    

    Also was ich wollte, als ich das geschrieben habe war, dass in dem Vector das jeweilige Objekt der jeweiligen Klasse (ElemKreis oder ElemRechteck) gescpeichert wird, weil sie ja von ElemGeomForm abgeleitet worden sind, somit sind sie ElemGeomForm-s. Der Iterator ist dann der Zeiger auf das jeweilige Element im Vector.

    Stimmt das so?

    Ich wollte 'in meinem ersten Beitrag' nicht lügen, ich versteh es so 😉



  • Du solltest Code in deinem Editor schreiben, compilieren und dann diesen Code hierein kopieren und mit deinen Compiler- /Laufzeitfehlern kommen.
    Nicht irgendwelche spontan zusammengedichtete falschen Codefetzen hier reintippern. Du vertauschst Namen und Typen, Deklarationen und Aufrufe, wir lesen hier nur totalen Unsinn.



  • Cordula schrieb:

    ElemVec.push_back(new ElemKreis(string Label_, string Farbe_));

    Das wird sicher nicht gehen. Der Vector reserviert doch für dich den Speicher. Ansonsten müsstest du Zeiger in den Vector legen, dann würde es wenigstens Sinn machen.

    ElemVec.push_back(ElemKreis(string Label_, string Farbe_));
    

    Wie kann ElemKreis in einen vector <ElemGeomForm>? Ist der abgeleitet?

    Welche Fehlermeldungen hast du denn?



  • @brotbernd: Genau das mach ich, zum Testen nehme ich aber halt nur den Teil aus dem ganzen Code der mich gerade interessiert und schreibe ein kleines Testprogramm, an dem ich ruhig rumprobieren kann, ohne dass ich meinen sonst lauffähigen Code aus Versehen verstümmel (alles schon da gewesen ..) Die Kritik über die Compiler-Fehlermeldungen werd ich mir aber zu Herzen nehmen.

    @HighLigerBiMBam: ElemKreis erbt von ElemGeomForm

    class ElemKreis:public ElemGeomForm
    

    - also denke ich, er müßte auch in dem Vector mit Objekten vom Typ ElemGeomForm eingefügt werden können - oder nicht?

    Fehlermeldung:

    c:\documents and settings\cordula\my documents\visual studio 2008\projects\parser\parser\parser.cpp(208) : error C2664: 'std::vector<_Ty>::push_back' : cannot convert parameter 1 from 'ElemKreis *' to 'const ElemGeomForm &'
    1> with
    1> [
    1> _Ty=ElemGeomForm
    1> ]
    1> Reason: cannot convert from 'ElemKreis *' to 'const ElemGeomForm'
    1> No constructor could take the source type, or constructor overload resolution was ambiguous

    Gut, der Fehlermeldung nach offensichtlich nicht - aber warum nicht? ElemKreis 'ist ein' ElemGeomForm?



  • Der Fehler kommt wegen new!!! Siehe mein Post.



  • Super, dann war meine Denkweise doch nicht ganz so falsch. Danke.

    Die 254 Fehler die dann kamen hab ich auf 28 reduziert. Und folgende Fehlermeldung find ich sehr unverständlich:

    1>c:\documents and settings\cordula\my documents\visual studio 2008\projects\parser\parser\parser.cpp(238) : error C2440: '<function-style-cast>' : cannot convert from 'std::string' to 'Parser::FindObj'
    1> Source or target has incomplete type

    Es geht um ein Functor, der den Predicate für das find_if definiert:

    struct FindObj{
    private:
    	string SearchString_;
    public:
    	FindObj(string Name) : SearchString_(Name){}
    	//bool operator()(const ElemGeomForm& findForm){
    	bool operator()(const ElemGeomForm& findForm){
    		return findForm.Label == SearchString_;
    	}
    };
    

    und das ruf ich auf mit

    string Label_ = FileLine.substr(18, 50);
    vector<ElemGeomForm>::iterator itElem = find_if(ElemVec.begin(), ElemVec.end(), FindObj(Label_));
    

    Fällt's auf wie viel Mühe ich mir gegeben habe alles ausführlich und übersichtlich zu posten? 😉



  • Hast du findobj.h oder wo FindObj auch immer definiert ist, included?
    Falls nein: schnell nachholen.

    HighLigerBiMBam schrieb:

    Der Fehler kommt wegen new!!! Siehe mein Post.

    Du must dich nicht mit fremden Federn schmücken, der Hinweis ist schon im zweiten Post angebracht.

    Dein vector<ElemGeomForm> provoziert aber ein Slicing-Problem, wenn du ein ElemKreis hinzufügst. Ist vielleicht etwas schwierig zu verstehen aber dein ElemKreis wird nur als ElemGeomForm gespeichert. Umgehen kannst du das Problem, wenn du den Vektor als vector<ElemGeomForm*> definierst und die Objekte trotz allem wieder mit new hinzufügst.

    PS: dein Post ist wirklich sehr übersichtlich.



  • mabmib schrieb:

    Du must dich nicht mit fremden Federn schmücken, der Hinweis ist schon im zweiten Post angebracht.

    Ja wurde aber nicht verstanden.



  • @mabmib:
    Die Deklaration von struct ist in dem Header, ist eingefügt, und die Definiton steht über dem main, daran liegt es nicht.

    Das mit dem Slicing war wirklich ein Tick zu komplex .. Aber ok, hab ich geändert. Wie kann ich dann die Attribute von dem Objekt auf dem der Iterator zeigt ändern? Mit

    (*itElem).Farbe = FileZeile.substr(18, 50);
    

    geht es ja nicht mehr, weil der Iterator auf einem Zeiger zeigt und nicht auf dem Objekt.



  • #include <vector>
    #include <algorithm>
    
    class Element
    {
    public:
    	Element(const std::string& label):
    	  label_(label), farbe_(0)
    	{}
    	const std::string& Label() const {return label_;}
    
    	void SetFarbe(int farbe)
    	{
    		farbe_ = farbe;
    	}
    private:
    	std::string label_;
    	int farbe_;
    };
    
    class Kreis : public Element
    {
    public:
    	Kreis(const std::string& label, double center, double radius) :
    	  Element(label), center_(center), radius_(radius)
    	{}
    private:
    	double center_, radius_;
    };
    
    struct FindLabel
    {
    	FindLabel(const std::string& label) : l(label){}
    	bool operator()(const Element* e) const{
    		return e->Label() == l;
    	}
    	std::string l;
    };
    
    template<typename T> void Deleter(T* e)
    {
    	delete e;
    }
    
    int main(int argc, char* argv[])
    {
    	using namespace std;
    	typedef vector<Element*> ElementVector;
    
    	ElementVector elements;
    	elements.push_back(new Kreis("Rund", 0, 1.23));
    	elements.push_back(new Kreis("Piiii", 0, 3.14));
    
    	ElementVector::const_iterator cit = find_if(  // konstanter Iterator. kann nix ändern. nur 'lesen'
    		elements.begin(), elements.end(), FindLabel("Rund"));
    
    	if (cit != elements.end())  // etwas gefunden?
    		cout << (*cit)->Label() << "\n";
    
    	ElementVector::iterator it = find_if(  // nicht konstanter Iterator. kann Farbe ändern
    		elements.begin(), elements.end(), FindLabel("Piiii"));
    
    	if (it != elements.end())  // etwas gefunden?
    		(*it)->SetFarbe(3);
    
    	// Alles löschen!
    	for_each(elements.begin(), elements.end(), Deleter<Element>);
    
    	return 0;
    }
    

    Einfach mal angucken und drüber nachdenken. Wenn Du es verstanden hast und nachbauen konntest kannst du dich mal informieren was man statt typedef vector<Element*> ElementVector; besser macht (z.B. typedef vector<shared_ptr<Element> > ElementVector; oder boost::ptr_vector<Element> )



  • Ich habe es mir so lange angeschaut, dass ich es fast auswendig kann. Ist mir zwar nicht alles glasklar was du gemacht hast, aber ich hab meinen Code dementsprechend geändert. Ungefähr so:

    struct FindLabel{
    	string SearchLabel_;
    	FindLabel(string Label_) : SearchLabel_(Label_){} 
             // Fehler: 'Label_' : undeclared identifier
    	bool operator()(Element* findElem){
    		return findElem->Label == Label_;
    	}
    };
    

    Was erzielt man mit

    FindLabel(const std::string& label) : l(label){}
    

    Bzw., warum deklarierst du den Label als const string&?

    Sorry, ich hab so weit zurückgeschraubt bis ich etwas hatte was ich auch selbst schreiben könnte, es macht wenig Sinn abzuschreiben was ich nicht versteh. Nun ja, hier versteh ich nicht woher die Fehlermeldung kommt, ist auch nciht besser.

    class Kreis : public Element
    {
    public:
        Kreis(const std::string& label, double center, double radius) :
          Element(label), center_(center), radius_(radius)
        {}
    
        void SetTransparenz(int Transparenz){
            Transp = Transparenz;
        }
    
    private:
        double center_, radius_;
    };
        int Transp; // Transparenz 0(min) - 5(max)
    
        ElementVector::iterator it = find_if(  // nicht konstanter Iterator. kann Farbe ändern
            elements.begin(), elements.end(), FindLabel("Piiii"));
    
        if (it != elements.end())  // etwas gefunden?
            (*it)->SetTransparenz(4); // Fehler: is not a member of 'Element'
    

    (Wie) Kann ich die Set-Methode der Unterklasse auf dem gefundenen Objekt anwenden? Bzw., dieser Fehler kommt erst nach dem ersten, von daher könnte er auch irrelevant sein.


Log in to reply