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



  • 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