Kritik und Optimierungen (Klassen mit Operatoroverloading)



  • ok , und wieso kann man:

    foo operator +(foo const& a, foo const& b) {
        foo tmp = a;
        tmp += b;    // hier greifen wir auf foo::operator += zu
        return tmp;
    }
    

    nicht in einer Klasse deklarien? muss es global sein um es zu benutzen? Dachte ich hätte den operator schon mal in einer klasse verwendet!



  • Klar kannst du den Operator auch in der Klasse definieren (in dem Fall entfällt der erste Parameter und du nutzt *this stattdessen) - der Grund, warum du es nicht tun solltest, sind die impliziten Konvertierungen:

    class myNumber
    {
    public:
      myNumber(int);
    ...
      //a:
      myNumber operator+(const myNumber& r);
    };
    
    //b:
    myNumber operator+(const myNumber& l, const myNumber& r);
    
    ...
    myNumber a,b,c;
    
    c=a+b;//klappt immer
    c=a+1;//klappt auch (wandelt 1 in ein myNumber um und addiert)
    c=1+b;//klappt nur mit Variante (b)
    c=1+2;//klappt ;) (Bonusfrage: Kannst du mir sagen, welche Operatoren/Methoden hier verwendet werden?)
    

    Kritisch ist in dem Beispiel die Anweisung c=1+b; - der Compiler darf keine impliziten Umwandlungen für this anwenden, deshalb lässt sich sowas mit dem Member-Operator nicht umsetzen.



  • "1+2" : Denk mal globater + operator der foo klasse oder der std. von int

    wobei noch der Zuweisungoperator gebruach wird oder? dereiname von Foo auf Foo zuweisen kann , udn von int auf Foo!!

    c= a+b;
    "a+b" : operator+ (l,r) dann das ergebenis (ab) und "c=ab" operator=(r).

    Hier mein code up-to-date:

    class CRoute{
    
    		CTypedPtrArray<CPtrArray,CGroup*> m_paNode;
    		int m_i;
    
    	public:
    		//Konstruktoren
    		CRoute(CGroup *p){
    			m_paNode.Add(p);
    		}
    		CRoute(){
    			//Kein Tiefes löschen notwendig da nur Referenzen
    			m_paNode.RemoveAll();
    		}
    		CRoute(CRoute &oScr){
    			this->m_paNode.Append(oScr.m_paNode);
    		}
    		//Destruktor
    		~CRoute(){
    			m_paNode.RemoveAll();	
    		}
    		//Anzahl der knoten in einer Route
    		inline int GetSize() const{ 
    			return m_paNode.GetSize(); 
    		}
    		//Zugriff auf elemente der Route über Index		
    		inline CGroup& CRoute::operator[] (const size_t iIndex){
    			return *(m_paNode.ElementAt(iIndex));
    		}
    		//Route an Route anhängen!
    		void CRoute::operator+(CRoute *&p){
    			for(int k=0; k< p->GetSize(); k++)
    				this->m_paNode.Add(&((*p)[k]));
    		}
    		//Knoten an Route anhängen
    		void CRoute::operator+(CGroup *&p){
    			this->m_paNode.Add(p);
    		}
    		/*Route einer anderne Route zuweisen (Tiefe Kopie nich notwendige, da die Routen nur auf
    		Knoten des Baumen zeigen*/
    		CRoute& CRoute::operator=(/*const*/ CRoute &oScr){
    			if(&oScr== this)
    				return *this;
    			ASSERT(&oScr!=NULL);
    			this->m_paNode.RemoveAll();
    			for(int i=0; i< oScr.GetSize(); i++)
    				this->m_paNode.Add(&(oScr[i]));
    			return *this;
    		}
    		//Route an vorhanden Route anfügen
    		CRoute& CRoute::operator+=(/*const*/ CRouteSector::CRoute &oScr){
    			if(&oScr==NULL)
    				return *this;
    			for(int k=0; k< oScr.GetSize(); k++)
    				this->m_paNode.Add(&(oScr[k]));
    			return *this;
    		}
    		//Route an vorhanden Route anfügen
    		CRoute& CRoute::operator+=(CGroup &oScr){
    			if(&oScr==NULL)
    				return *this;
    			//for(int k=0; k< oScr.GetSize(); k++)
    			this->m_paNode.Add(&oScr);
    			return *this;
    		}
    		//Route ausgeben
    		void DEBUG_TRACEOUT(){
    			TRACE("Pfad ADR.: %i\n",(int)this);
    			for(int i=0; i< this->GetSize(); i++){
    				CGroup *p =this->m_paNode.ElementAt(i);
    				TRACE("ADR.: %i NODE: %s\n", (int)p, p->GetName()); 
    			}
    		}
    	};
    

    wenn ich const bei den übergabeparameter anfeben (siehe /* const */) kommst aber immer der compilerfehler : d:\Multithreading\multithread\multithread\ProcGraph.h(368): error C2678: binary '[' : no operator found which takes a left-hand operand of type 'const CRouteSector::CRoute' (or there is no acceptable conversion)



  • BorisDieKlinge schrieb:

    "1+2" : Denk mal globater + operator der foo klasse oder der std. von int

    Fast richtig: "c=1+2;" verwendet (in dieser Reihenfolge) den op+ für int's (eingebaut), den Ctor myNumber(int) und den op= (entweder von dir geschrieben oder implizit angelegt)

    wenn ich const bei den übergabeparameter anfeben (siehe /* const */) kommst aber immer der compilerfehler : d:\Multithreading\multithread\multithread\ProcGraph.h(368): error C2678: binary '[' : no operator found which takes a left-hand operand of type 'const CRouteSector::CRoute' (or there is no acceptable conversion)

    Vielleicht solltest du auch eine const-Version deines operator[] bereitstellen 😉



  • oh man das versteh ich nich mit den const. ich weis zwar was sie bedeuten, aber in welchem zusammmenhang sie hinsichtlich der funltionen udn operatoren stehen müssen ist so ein wirrwarr..

    Trozdem noch mal ein Dankeschön zwischendruch an CStoll:) Kommst dir sicher vor wie im kindergarten:)

    du meinst:

    //Zugriff auf elemente der Route über Index        
            inline const CGroup& CRoute::operator[] (const size_t iIndex){
                return *(m_paNode.ElementAt(iIndex));
            }
    

    ??

    kann ich dann überhaupt nicht const Objekte der klasse CRoute an die funltionen übergeben?



  • Nein, ich meinte

    inline CGroup CRoute::operator[] (size_t iIndex) const
    {
        return *(m_paNode.ElementAt(iIndex));
    }
    

    (an dieser Stelle gilt das const für *this - und bewirkt letztendlich, daß du den Operator auf 'const CRoute' Objekte anwenden darfst)



  • ok dann hab ich die const dinge doch nich so verstanden:

    wenn ich die parameter des operator:

    CRoute& CRoute::operator=(/*const*/ CRoute &oScr){ ..}
    

    const übergebe,
    undc ich später:

    CRoute a;
    
    CRoute b;
    
    a=b;
    

    dann ist ja b nicht const oder???? aber der opertor erwarte ein const!



  • Ein nicht-konstantes Objekt kann problemlos als konstantes Objekt angesehen werden (umgekehrt gilt das nicht). Das heißt, für den operator=(const CRoute& oSrc); ist das übergebene Objekt konstant (und daß du letztendlich die Möglichkeit hättest, dieses Objekt in anderen Teilen des Programms zu ändern, ist ihm egal). Also darf er auch nur Methoden des Objekts aufrufen, die es nicht verändern dürfen.

    Dabei ist es dem Compiler egal, was du wirklich in der Methode machst (das könnte sich ja später ändern). Wichtig ist nur die Deklaration - mit dem const hinter der Parameterliste sagst du dem Compiler, daß diese Methode ihr this-Objekt nicht ändern wird (und wenn sie es doch macht, warnt dich der Compiler).

    PS: Du hast dir aber einiges vorgenommen 😉



  • Du kannst ein non-const Objekt jederzeit in ein konstantes Objekt implizit umwandeln. Macht ja auch Sinn, denn const sagt ja aus dass das Objekt nicht geändert wird - und es spricht nichts dagegen ein änderbares Objekt nicht zu ändern 😃

    Umgekehrt funktioniert es nicht - ein konstantes Objekt kann nicht geändert werden und auch nicht ohne böse Casts in ein non-const Objekt umgewandelt werden.



  • aha, dann sag ich dem compiler nur das ich das obejekt in der funktion (operator) nich ändern werde.. aber was bringt ihm das?

    @CStoll: Ja nehm mir immer viel vor. Bin ein kleiner Perfektionist. Wenn was nich geht will ich wissen warum, und wenn was geht auch;)

    Und leider klappt das mit dem Konst nich. das MFC Array hat was dageggen wenn ich den operator[] (..) const {..} schreibe:

    d:\Multithreading\multithread\multithread\ProcGraph.h(544): error C2662: 'CTypedPtrArray<BASE_CLASS,TYPE>::ElementAt' : cannot convert 'this' pointer from 'const CTypedPtrArray<BASE_CLASS,TYPE>' to 'CTypedPtrArray<BASE_CLASS,TYPE> &'
            with
            [
                BASE_CLASS=CPtrArray,
                TYPE=CGroup *
            ]
            and
            [
                BASE_CLASS=CPtrArray,
                TYPE=CGroup *
            ]
            and
            [
                BASE_CLASS=CPtrArray,
                TYPE=CGroup *
            ]
    


  • BorisDieKlinge schrieb:

    aber was bringt ihm das?

    Nunja, am wichtigsten: Wenn das Objekt selbst bereits konstant ist, weiss er dass Du die Methode trotzdem aufrufen darfst.

    Nicht weniger wichtig, aber für den Programmierer vielleicht in erster Linie nicht so interessant: Optimierungspotenzial.



  • BorisDieKlinge schrieb:

    Und leider klappt das mit dem Konst nich. das MFC Array hat was dageggen wenn ich den operator[] (..) const {..} schreibe:

    Ja, CTypedPtrArray::ElementAt() ist auch nicht-konstant - die const-Version davon nennt sich GetAt() 😉

    (habe ich schon erwähnt, daß du besser umsteigen solltest auf std::vector<>?)



  • ja das hast du oder andere schon Erzähl. Problem : vector kann keine Polymorphen klassen enhalten oder?? Wobei ich mich frage warum...



  • BorisDieKlinge schrieb:

    Problem : vector kann keine Polymorphen klassen enhalten oder??

    Dann nimmst du halt einen vector<CGroup*****>, der kann das 😉 (OK, die Speicherverwaltung mußt du manuell übernehmen, aber afaik sind die MFC-Arrayklassen auch nicht viel besser damit)
    Alternativ kannst du mal bei Boost vorbeischauen, dort gibt es auch Versionen der Standard-Container, die mit Zeigern arbeiten.



  • hmm ok werd mir den vector mal anschaun? was für vorteile hat der gegenüber MFC, auser das er nich von MFC ist? schneller?



  • BorisDieKlinge schrieb:

    auser das er nich von MFC ist?

    Das zieht den Vorteil nach sich, dass der std::vector überall vorhanden ist, wo eine C++ Standardlib vorhanden ist... während die MFC halt (a) Win-only ist und (b) auch da erst käuflich erworben werden muss.



  • ok gut kann man auc hauf einen schlage die elemente von vetor a in vector b anhängen?



  • BorisDieKlinge schrieb:

    ok gut kann man auc hauf einen schlage die elemente von vetor a in vector b anhängen?

    Ja, natürlich. Schau Dir doch einfach mal in einer Doku die Methoden und Operatoren von 'std::vector' an. Das Anhängen geht mit op+= bzw. 'insert'.



  • Man kann auch einfach einen vector mit smart-pointern (z.B. shared_ptr von Boost) verwenden, dann ist die Speicherverwaltung auch gleich vernünftig.

    Der boost::shared_ptr hat einen Referenzzähler, d.h. bei jedem Kopieren des Zeigers, wird der interne Referenzzeiger um 1 erhöht. bei jedem delete um 1 verringert. Wenn der Referenzzeiger 1 ist und man delete aufruft, wird der Speicher tatsächlich freigegeben.

    Somit kann man den Zeiger im Vector speichern. Wenn der Vector gelöscht wird, wird das delete von dem im Vector gespeicherten Element aufgerufen, aber wenn man noch irgendwo anders eine Referenz auf diesen Zeiger hat, kann man später (also nach dem Löschen des Vectors) damit weiterarbeiten, da ja nur der Referenzzähler heruntergezählt wurde (Der Zeiger ist also noch gültig).

    Ein anderes Problem ist, dass der Vector bei "normalen" Zeigern einfach nur den Zeiger, nicht aber das Objekt löscht (Speicherleck)

    Gruß Paddy



  • Ja gut das war bei CTypePtrArray auch immer so. Aber das hab ich bisher noch hinbenkommen, zudem verwende ich Pointer Array auch nur als referenz speicher , die objekte udn desen Zeiger sind oft in einem extra Array gespeichert, das die objetk auch wieder löscht!!


Anmelden zum Antworten