Operatorüberladung



  • Wenn ich nen Operator überlade, z.B:

    class Bruch {
    private:
       int zaehler;
       int nenner;
    public:
       Bruch(int z=0, int n=1);  
       friend Bruch operator*(const Bruch& b1, const Bruch& b2);
    };
    

    Dann kann ich ja sowas schreiben:

    Bruch b1, b2;
    Bruch b3 = b1 * b2;
    

    Aber auch sowas:

    Bruch b1, b2, b3;
    Bruch b2 = b1 * 2;
    Bruch b3 = 7 * b2;
    

    Diese ganzen impliziten Typumwandlung kosten ja Zeit. Sollte man deshalb aus Effizienzgründen Operatoren für jeglich erdenklichen Fall implementieren?
    Also auch sowas:

    friend Bruch operator*(const Bruch& b1, int zahl);
    friend Bruch operator*(int zahl, const Bruch &b1);
    // usw für double und und und
    

    Da komme ich ja auf 1000 Methoden. Was sagt ihr dazu?



  • Nein solltest du nicht, hat keinen Sinn.

    MfG SideWinder



  • Diese ganzen impliziten Typumwandlung kosten ja Zeit. Sollte man deshalb aus Effizienzgründen Operatoren für jeglich erdenklichen Fall implementieren?
    Also auch sowas:

    Mach den Operator global. Bau den Konstruktor inline, und verlass dich auf deinen Compiler!

    mfg



  • mögt ihr keine performance oder ist das dekandenz?



  • Ich würde nur noch ne Möglichkeit für double anbieten, int kann ja implizit in double
    umgewandelt werden und ich denke das drückt die Performance nicht wirklich.



  • SirLant schrieb:

    Ich würde nur noch ne Möglichkeit für double anbieten, int kann ja implizit in double
    umgewandelt werden und ich denke das drückt die Performance nicht wirklich.

    nen bruch aus nem int machen ist einfach. zähler wird der int, nenner wird 1.
    en bruch aus nem double machen ist sehr gemein. wie machste denn aus 5.125 den bruch 21/8?
    der weg, nen operator für nen double zuzulassen, um sich den fpr den int zu sparen könnte erheblche zusatzkosten tragen.
    nee, man muß anders überlegen.
    kostet die implizite erstellung eines double aus nem int was beim multiplizieren?

    annahme: ggt wird nicht inline sein und heftig teuer sein.

    int ggt(int a,int b){
    	if(a<b)
    		swap(a,b);
    	for(;;){
    		int tmp=a%b;
    		if(tmp==0)
    			return b;
    		a=b;
    		b=tmp;
    	}
    }
    

    diese divisionen sind so ziemlich das einzig teure.

    annahme:

    class Bruch{
    private:
    	int z;
    	int n;
    public:
       Bruch(){
    	}
    	Bruch(int zaehler):
    	z(zaehler),
    	n(1){
    	}
    	Bruch(int zaehler,int nenner):
    	z(zaehler),
    	n(nenner){
    	}
    	void kuerzen(){
    		int g=ggt(z,n);
    		z/=g;
    		n/=g;
    	}
       friend Bruch operator*(Bruch const& a,Bruch const& b){
    		Bruch r(a.z*b.z,a.n*b.n);
    		r.kuerzen();
    		return r;
    	}
    	friend Bruch operator*(Bruch const& a,int b){
    		Bruch r(a.z*b,a.n);
    		r.kuerzen();
    		return r;
    	}
    	friend ostream& operator<<(ostream& out,Bruch const& b){
    		return out<<b.z<<'/'<<b.n;
    	}
    };
    

    kann man den op*(bruch,int) billiger machen als mit einem ggt-aufruf? der op*(Bruch,Bruch) hat auch nur einen. die kosten sind also nicht viel schlimmer.

    gespart ist ein multiplikation mit b.z in Bruch r(a.z*b.z,a.n*b.n);, aber dieses b.z ist dem optimierenden compiler als konstant 1 bekannt. fazit:
    friend Bruch operator*(Bruch const& a,int b)
    brauchte gar nix.

    das heißt aber nicht, daß alles nix bringt. man sollte schon im einzelnen überlegen.
    vermutlich sollen die brüche normiert gehalten werden mit nichtnegativen nenner. bleiben dir brüche stabil? kann man manchmal nen check (zum beispiel bei multiplikation mit nem unsigned) sparen?
    ich würde zusatzversionen immer anbieten, wenn ein fitzelchen performache raussprimgt.



  • ca 2.000.000.000 Befehle pro Sekunde. was ist da der Unterschied zwischen 10 und 20 Befehlen? 😉



  • Praktikant schrieb:

    ca 2.000.000.000 Befehle pro Sekunde. was ist da der Unterschied zwischen 10 und 20 Befehlen? 😉

    Ich denke, du meinst eher 2*10^9 Takte. Takte != Befehle



  • (hmm, Denkfehler geloescht)

    Trotzdem nochmal ein Nachtrag. Wieso sollte man wollen, einen Bruch mit einem Double zu multiplizieren? Bruch und Double decken verschiedene Untermengen der rationalen Zahlen ab. Der einzige Vorteil von Bruch gegenueber double ist doch, dass Zahlen wie 1/3 praezise abgespeichert werden koennen (und dafuer andere nicht). Wenn man nun einen Bruch mit double(1/3) multipliziert, ist dieser Vorteil zunichte gemacht.



  • Praktikant schrieb:

    ca 2.000.000.000 Befehle pro Sekunde. was ist da der Unterschied zwischen 10 und 20 Befehlen? 😉

    wenn man nur eine rechnung macht, ist es für den user egal.
    bestellt der user aber 1.000.000.000 rechnungen, fällt ihm auf, wenn das prog 20 statt 10 sekunden rechnet.
    typischerweise fallen so primitive sachen wie multiplikationen in ganz inneren schleifen an. ist das nicht traurig?


Anmelden zum Antworten