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



  • 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.



  • Ehrlich gesagt denke ich, dass du dir ganz dringend noch mal ein Grundlagen Buch ansehen solltest. Vielleicht kannst du schon eine andere Sprache und probierst ein bischen C++ aus? Wenn Du noch keins hast, besorg dir mal ein gutes Buch (C++ Primer z.B.).

    Zu Deinen Fragen:
    Instanzen von Klassen übergibt man meistens oder fast immer als konstante Referenz (const string&). Das ist, was in den vielen anderen Sprachen automatisch passiert, da dort alles eine Referenz ist. In C++ bedeutet die Signatur (string label) aber, dass hier eine Kopie vom string übergeben wird. Um die Kopie einzusparen übergibt man eine Referenz. Und const, damit man das Original nicht verändert kann.

    ElementVector ist ein vector<Element*>. Ein ElementVector::iterator ist ein vector<Element*>::iterator. Der * Operator des ElementVector::iterator gibt eine Referenz auf ein Element* zurück.

    (*it)->SetTransparenz(4);
    

    ruft also die Methode SetTransparenz mit einem Element* auf. Der Compiler sucht also Element::SetTransparenz. So eine Funktion existiert nicht, denn SetTransparenz hast du in der Kreisklasse definiert. Wenn Du SetTransparent mit einem Element* aufrufen möchtest, muss diese Methode in der Schnittstelle von Element vorhanden sein. Das bedeutet dann aber, dass alle Elemente eine Transparenz zugeordnet bekommen können. Wenn das eine reine Kreiseigenschaft ist, musst du dir etwas anderes überlegen. Mit einem Elemen* hast du nur Zugriff auf die Element Schnittstelle, du kannst also nur das machen, was alle Arten von Elementen betrifft.



  • Das könnte man z.Bsp. mit einem dynamic_cast lösen (ist aber eigentlich kein guter Stil).

    if (it != elements.end()) { // etwas gefunden?
        Kreis* kreis = dynamic_cast<Kreis*>(*it); // versucht Element* in Kreis* zu verwandeln
        if( kreis != 0) // Ja es ist ein Kreis
            kreis->SetTransparenz(4);
    }
    


  • @brotbernd: Ja, du hast es auf den Punkt gebracht. Ich bin Newbie in C++, und weil in dem Buch durch welches ich mich durchgearbeitet habe nichts demgleichen vorkam, hab ich es auch nicht vermisst, weil ich es aus meinen Sprachen nicht kenne. Unglücklich - aber aus Fehlern lernt man.

    Für den Fall dass jemand an derselben Stelle Schwierigkeiten hat:
    Das 'Problem' habe ich anders gelöst, ich habe die Objekte der abgeleiteten Klassen jeweils in einen eigenen Vektor gepackt. Das hat sich auch für den weiteren Verlauf als vorteilhaft erwiesen, da man auf der Suche nach einem Objekt nur den Vektor genau dieser Objekte durchsuchen muß und nicht den (möglicherweise um ein Mehrfaches) größeren Vektor aller Objekte.

    Es belibt eine Stelle offen, und den Fehlre bekomme ich nicht mehr weg (was ich einige Posts weiter oben geschrieben habe)

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

    aufgerufen mit

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

    gibt folgenden Fehler zurück:

    1>error C2440: '<function-style-cast>' : cannot convert from 'std::string' to 'Parser::FindObj'
    1> Source or target has incomplete type

    Fällt jemandem was dazu ein?



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

    aufgerufen mit

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


  • Aaaalso:

    Du hast doch hier
    vector<ElemGeomForm>::iterator itElem = find_if(ElemVec.begin(), ElemVec.end(), FindObj(Label_));
    einen Vektor mit Elementen vom Typ ElemGeomForm.
    Hier:
    vector<ElemGeomForm>::iterator itElem = find_if(ElemVec.begin(), ElemVec.end(), FindObj(Label_));
    wird der Operator () des temporär erstellten FindObj - Objektes aufgerufen, dem ein solches ElemGeomForm als Parameter mitgegeben wird.
    Hier:
    bool operator()(const ElemGeomForm& findForm){
    return findForm->Label == SearchString_;
    }
    tust Du aber so, als hättest Du nicht ein ElemGeomForm - Element, sondern einen Zeiger auf ein solches - Du benutzt nämlich den -> Operator anstelle des . Operators.

    Da ich anfangs des Threads den Eindruck gewonnen habe, Du weißt selber nicht, was von beiden nun richtig ist, kann ich Dir mehr auch nicht dazu sagen.
    Aber eines von beiden muß falsch sein.



  • Ich hab im Laufe letzter Woche ziemlich oft was an diesem Vector verändert, aktuell ist es ein Vector aus Zeigern, der auch den Fehler verursacht. Ich definiere dann das struct so:

    struct FindObj{
    private:
        string SearchString_;
    public:
        FindObj(string Name) : SearchString_(Name){}
        bool operator()(ElemGeomForm* findForm){
            return findForm->Label == SearchString_;
        }
    };
    

    und ruf es auf mit

    std::vector<ElemGeomForm*>::iterator itElem = find_if(ElemVec.begin(), ElemVec.end(), FindObj(Label_));
    

    Also wird das erstellte FindObj-Objekt aufgerufen, indem ihm der Zeiger auf dem jeweiligen Objekt mitgegeben wird. Kann das der Grund sein? Es heißt ja "cannot convert from 'std::string' to 'Parser::FindObj'". Aber wie kann ich das korrigieren?


Anmelden zum Antworten