PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : C++: Operator überladen


pest
2012-10-02, 16:15:39
Ich bekomme es gerade nicht gebacken einen Zuweisungsoperator zu überladen

ich habe die Klasse State

class State
{
public:
State(int dim):n(dim)
{
x=new double[n];
f=0;
};
State &operator=(const State &s)
{
if (this != &s) LoadState(&s);
return *this;
}
void LoadState(const State *src)
{
memcpy(x,src->x,sizeof(double)*n);
f=src->f;
}
~State()
{
delete []x,x=0;
}
int n;
double *x;
double f;
};


in einer anderen Klasse läuft folgender Code in einer Schleife

double dE=NewState->f-CurState->f;
if (dE<0)
{
CurState=NewState;
} else
{
double p=exp(-dE/T);
if (p>Rand::RandDouble())
{
CurState=NewState;
}
};
if (CurState->f < BestState->f)
{
BestState=CurState;
}


da kommt aber Müll raus, irgendwie scheinen BestState und CurState auf die gleichen Adressen zu zeigen :confused:

ersetze ich alle Zuweisungen der Klasse "State" mit der im Operator aufgerufenen Funktion "LoadState" geht es, also

double dE=NewState->f-CurState->f;
if (dE<0)
{
CurState->LoadState(NewState);
} else
{
double p=exp(-dE/T);
if (p>Rand::RandDouble())
{
CurState->LoadState(NewState);
}
};
if (CurState->f < BestState->f)
{
BestState->LoadState(CurState);
}


woran liegt das?

del_4901
2012-10-02, 16:19:04
Der Zuweisungsoperator ist nur fuer Referenzen ueberladen und nicht fuer Pointer. Wenn du einen Pointer einen anderen zuweist, dann wird einfach nur die Adresse kopiert. Wenn du irgendwie kannst, dann arbeite doch mit Referenzen. Ich finde das eh schoener und untendrunter hantiert der Compiler auch mit Pointern.

pest
2012-10-02, 17:27:16
danke, ja das macht Sinn

wenn ich eine Referenz an eine Funktion übergebe, wird dann eine Kopie auf dem Stack angelegt? und kann ich wie gewohnt die Referenz per "&State" als Zeiger übergeben?

del_4901
2012-10-02, 17:32:42
danke, ja das macht sinn

wenn ich eine referenz an eine funktion übergebe, wird dann eine kopie auf dem stack angelegt?
Nein, deswegen uebergibt man auch bei grossen Objekten gerne const Referenzen. Weil das Verhalten ungefaehr das gleiche ist wie per Value zu uebergeben (nur halt versteckt untendrunter mit Zeigern).
Ueberleg dir aber genau warum du den Zuweisungsoperator ueberladen moechtest (mach dir Notizen, und waege fuer und wieder ab). Wuerde ein anderer das dann genauso verstehen?

PS: Weil man es kann ist kein guter Grund. ;)


oder kann ich wie gewohnt die Referenz per "&State" als Zeiger übergeben?

das wird dann ohne & beim call geschrieben dafuer dann aber mit & in der signatur. C# ist da wesentlich schoener weil man beides beim call und in der signatur eindeutig erkennen kann.

pest
2012-10-02, 17:41:36
PS: Weil man es kann ist kein guter Grund. ;)


ich will einfach nicht so viel schreiben ;(


das wird dann ohne & beim call geschrieben.

ich habe nie direkt mit referenzen gearbeitet- ich habe angst, dass ich im code dann durch das fehlen des "->" operators irgend etwas übersehe.

außerdem habe ich gerade keine ahnung wie ich mein bisheriges procedere,
also klasse mit new erstellen weil dynamische parameter existieren und dann mit zeigern arbeiten, auf referenzen umstelle.

vielleicht kann man das gänzlich anders lösen? also das "double *x" dynamisch sein soll.

jetzt würde ich es so machen

State *myState=new State(32);
State &StateRef=myState;
StateRef.DoSomething()

del_4901
2012-10-02, 17:49:41
ich will einfach nicht so viel schreiben ;(

Das ist ein ganz schlechter Grund, gute Funtions- und Variablenbenenung sind essenziell damit sich der Code gut liest.


ich habe nie direkt mit referenzen gearbeitet- ich habe angst, dass ich im code dann durch das fehlen des "->" operators irgend etwas übersehe.

außerdem habe ich gerade keine ahnung wie ich mein bisheriges procedere,
also klasse mit new erstellen weil dynamische parameter existieren und dann mit zeigern arbeiten, auf referenzen umstelle.

jetzt würde ich es so machen

State *myState=new State(32);
State &StateRef=*myState;
StateRef.DoSomething()
In dem falle wuerde ich dir davon abraten das mit Referenzen und dem Zuweisungsoperator zu loesen und einfach Funktionen zu verwenden.
Weil sich mit new Speicher zu hohlen und das dann in eine Referenz zu wrappen, wirft nur noch mehr Fragezeichen auf. Ich dache du kannst das alles in einem Scope loesen.

Es tut manchmal weh, aber ein guter Programmierer schmeisst auch sehr viel eigenen code weg.

vielleicht kann man das gänzlich anders lösen? also das "double *x" dynamisch sein soll.
Man koennte ein "Handle" nehmen um den double zu wrappen, aber das waehre auch Overkill. Nimm Funktionen und schreib dafuer ein bissle mehr, weil Jemand der das spaeter lesen muss hat von alldem keinen Mehrwert.

pest
2012-10-02, 17:58:37
ach es ist ja gut, dass ich jetzt frage, nachdem ich erst 200 Zeilen geschrieben habe.

für die Lösung des Problems "dynamischer Speicher" habe ich schon seltsame Konstrukte gesehen.

am Liebsten wäre mir ja eine Lösung der Art

class TolleKlasse
{
public:
State myState(32);
}

edit: mir fällt gerade ein, dass ich es in anderen Programmen so ähnlich mache. entweder als Template :>

Anscheinend funktioniert dass auch:

class TolleKlasse
{
public:
TolleKlasse():myState(32){};
State myState;
}

Jedenfalls mache ich das in einem anderen Projekt so - Oh Gott, ich muss wieder mehr coden :(

Vielleicht mache ich das Feld auch als Vektor und vergrößere es dann einfach wenn ich die Dimension des Vektors global kenne.

Wie löst du die sache mit dynamischen Feldern?

del_4901
2012-10-02, 18:24:32
ach es ist ja gut, dass ich jetzt frage, nachdem ich erst 200 zeilen geschrieben habe.
Ganz normal, passiert mir auch. Refactor was zu retten ist und schmeiss den Rest einfach weg.

für die lösung des problems "dynamischer speicher" habe ich schon seltsame konstrukte gesehen.

am liebsten wäre mir ja eine lösung der art

class TolleKlasse
{
public:
State myState(32);
}


vielleicht mache ich das feld auch als vektor und vergrößere dann einfach das feld wenn ich die dimension des vektors global kenne.

wie löst du die sache mit dynamischen feldern?

Bei sowas kann man ein ganz normales dynamisches Array verwenden (std::vector ... oder einfach mal schnell selber schreiben wenn man nur push und index op braucht)

In den States kannst du dann anstatt der adresse den index in dieses array hinterlegen. Wenn du diese Logic dann noch schoen kapselst (evtl. mit templates) hast du schon sowas wie ein Handle.

Wenn du mittendrin Objekte aus dem Array rausnehmen kannst, musst du dir noch ueberlegen wie du die Handles invalidieren kannst. Und dann vergibst du die Stelle beim naechsten mal einfach neu.

Aber auch hier gilt: Programmier NUR das was du JETZT wirklich brauchst um das AKTUELLE Problem zu loesen. Was spaeter sein koennte ist egal, da existiert der Code vllt. schon gar nicht mehr.

pest
2012-10-02, 18:33:15
ich frage mich halt immernoch, ob ein Konstrukt der Form "Klasse->Func()" langsamer ist als "Klasse.Func()"

in diesem Fall ist es irrelevant, aber ich habe mir bei dem zweiten Codebeispiel im vorrigen Post irgendwas dabei gedacht :?

Ich neige auch dazu bei Sachen wie Obj1->Obj2->Obj3->Func() mir erstmal nen Zeiger auf Obj3 zu basteln, wenn es in innere Schleifen geht.

del_4901
2012-10-02, 18:37:12
ich frage mich halt immernoch, ob ein Konstrukt der Form "Klasse->Func()" langsamer ist als "Klasse.Func()"

in diesem Fall ist es irrelevant, aber ich habe mir bei dem zweiten Codebeispiel im vorrigen Post irgendwas dabei gedacht :?

Ich neige auch dazu bei Sachen wie Obj1->Obj2->Obj3->Func() mir erstmal nen Zeiger auf Obj3 zu basteln, wenn es in innere Schleifen geht.

Wenn du dich solche Dinge fragst, dann schau dir das Assembly an. Du kannst auch nen Profiler nehmen und messen. Alles andere ist Spekulation, man hat zwar gewisse Erfahrungswerte irgendwann, aber es entwickelt sich dann auch so ein Bauchgefuehl, dass ist noch viel cooler. Da muss man dann aber trotzdem in der Lage sein darauf zu hoeren und das auch verifizieren.

Aquaschaf
2012-10-03, 09:00:59
Ich neige auch dazu bei Sachen wie Obj1->Obj2->Obj3->Func() mir erstmal nen Zeiger auf Obj3 zu basteln, wenn es in innere Schleifen geht.

Wenn du solche Aufrufe basteln musst, dann kann es durchaus Sinn machen sich über die Struktur noch einmal Gedanken zu machen :)

So eine "train wreck"-Aufrufkette deutet darauf hin dass der Aufrufer wahrscheinlich nicht nur seine direkten Nachbarn (z.B. Obj1) kennt, sondern auch Nachbarn seiner Nachbarn, und wiederum deren Nachbarn. Das macht den Code oft schwer verständlich und es wird schwer etwas daran zu verändern (eine Änderung an der Klasse Obj3 könnte in diesem Beispiel sehr hohe Wellen schlagen). Unit tests für Code der solche Aufrufe enthält werden auch schnell unhandlich.