Was alles const machen/ konstante Getter Funktionen
-
Hio!
Vielleicht habt ihr es ja schon bemerkt, ich arbeite gerade viel mit const herum
Dazu habe ich (hoffentlich die letzten) zwei Fragen:
Hier mal Beispielcode:class Vector { private: double xPosition; double yPosition; double zPosition; public: Vector(double pXKoord, double pYKoord, double pZKoord); Vector(const Vector &pPosition); virtual ~Vector(); void SetXKoord(double NewXKoord); void SetYKoord(double NewYKoord); void SetZKoord(double NewZKoord); double GetXKoord() const; double GetYKoord() const; double GetZKoord() const; double GetLength() const; void AddVector(const Vector &pVector); void SubVector(const Vector &pVector); void AddNumber(double pNumber); void SubNumber(double pNumber); };1. Frage: Wenn man schon strikt mit "const" umgeht, soll man dies auch in im Konstruktor oder z.B. in den Setter durchziehen? Also anstatt "void SetXKoord(double NewXKoord);" schreiben: "void SetXKoord(const double NewXKoord);"
Andere Beispielklasse:
class List { private: Vector *vector; List *next; List *last; List *firstEntry; int length; public: List(); void Add(Vector &Vector); //Der Vektor soll veränderbar sein Vector GetVector(); List* Next(); List* Last(); List* First(); void Delete(); int GetLength(); };2. Frage: Wenn ich den Getter vom Vektor konstant mache (also Vector GetVector() const;) dann ginge soetwas nicht mehr oder?:
list->GetVector()->SetXKoord(1000); oder?
-
Zu 1.:
Zitat von Pumuckl aus diesem Thread (2. Antwort):Objekte kleiner Typen (z.B. alle eingebauten Typen, also auch int) per Referenz zu übergeben ist zwar möglich, aber allgemein nicht üblich. Die Übergabe per const-Referenz spart zwar eine Kopie des jeweiligen Objekts, allerdings muss die Referenz (intern meist ein Zeiger) erstellt und bei jedem Zugriff dereferenziert werden. Das ist bei kleinen Datentypen teurer als eine Kopie.
Zu 2.:
Ich meine, das geht schon, bin mir aber nicht sicher, da das const hinter den Klammern meines Wissens nach nur bewirkt, dass innerhalb der geschweiften Klammern nix geändert werden kann, also z.B. nur ein Wert zurückgegeben werden kann.
-
*2 mal das Selbe gepostet*
-
Frage 1 bezog ich nicht direkt auf eine Referenz auf kleine Typen sondern allgemein. Wenn ich weiss, dass ich in einer Funktion den übergebenen Wert nicht ändern will/werde, dann kann ich die übergeben Parameter doch gleich auch konstant übergeben oder nicht?
also sowas:int main() { int a = 5; int b = 10; int c = Add(a,b); // oder direkt: int c = Add(5,10); std :: cout << c << std :: endl; } int add(const int a, const int b) { return a+b; }
-
Pille456 schrieb:
Frage 1 bezog ich nicht direkt auf eine Referenz auf kleine Typen sondern allgemein. Wenn ich weiss, dass ich in einer Funktion den übergebenen Wert nicht ändern will/werde, dann kann ich die übergeben Parameter doch gleich auch konstant übergeben oder nicht?
also sowas:int main() { int a = 5; int b = 10; int c = Add(a,b); // oder direkt: int c = Add(5,10); std :: cout << c << std :: endl; } int add(const int a, const int b) { return a+b; }Nein. Konstante Parameter mit Übergabe als Wert machen keinen Sinn.
Simon
-
Pille456 schrieb:
2. Frage: Wenn ich den Getter vom Vektor konstant mache (also Vector GetVector() const;) dann ginge soetwas nicht mehr oder?:
list->GetVector()->SetXKoord(1000); oder?Doch das geht, da das const nur bedeutet, dass die Liste nicht verändert wird. Das Objekt Vektor liegt als Kopie vor und die Kopie könnte man ändern.
Aber warum willst Du denn den X-Wert überschreiben, den Du gerade zusammen mit dem Vektor aus der Liste geholt hast? das macht doch keinen Sinn - oder? Schau Dir mal zu dem Thema Set-Methoden diesen Thread an. Jester und ich sind da verschiedener Meinung - also mache Dir selber ein Bild.Zum Thema der mathematische Vektor als Klasse findest Du hier einiges.
Gruß
Werner
-
Folgende Möglichkeiten gibts ja für die Parameterübergabe, ich fass mal zusammen wan und warum man welche davon verwendet:
const Referenz: du willst an dem übergebenen Objekt nichts ändern. deshalb const. lohnt wie oben schon zitiert für eingebaute Typen nicht, die übergibt man eher per value.
non-const value: du willst mit dem übergebenen Wert arbeiten (z.B. nicht-konstante Memberfunktionen), aber das ursprüngliche Objekt, das der Funktion als Argument übergeben wurde, nicht verändern. Du brauchst also eine nicht-konstante Kopie.
non-const Referenz: du möchtest ein Objekt ändern, das außerhalb der Funktion existiert. hier können/müssen referenzen auch auf eingebaute Datentypen benutzt werden. Die Möglichkeit, das externe Objekt zu ändern kostet dann eben ein bisschen mehr als ne Kopie.
const value: eigentlich würden nach der Liste hier nurnoch kleine Objekte (eingebaute Typen) landen, die du in der Funktion nicht verändern willst. Das ist aber nur eine Einschränkung an dich als Implementor der Funktion, nicht an den Aufrufer. Allgemein stellt eine Funktionsschnittstelle aber nur Garantien und Anforderungen an den Aufrufer dar, keine Implementationsdetails. const value macht man also allgemein nicht.Der Vollständigkeit halber der Spezialfall: Man stelle sich eine kleine Klasse vor, (z.B. eniziges Member ein char, das IST klein) die const-methoden hat, die du in der Funktion benutzen willst. Du möchtest es also const machen, aber eine Referenz ist teurer als eine Kopie. Du könntest eine non-const Kopie machen und bei Bedarf const_casts benuten. die Sind aber eklig und unschön. Zweiter Anlauf: du machst eine non-const Kopie und direkt nach Eintritt in die Funktion kopierst du das Ding in ein const-Objekt. Und hoffst dass der compiler das schnallt und die Kopie wegoptimiert. Sieht auch nicht so toll aus, und wer weiß schon so recht ob der Compiler das wirklich immer wegoptimieren kann... Also doch const value?
Antwort: nein, bzw. nur unter ganz bestimmten Bedingungen.
- den Code durch die extra-Kopie oder const_casts zu verhunzen ist unschön.
- die Schnittstelle durch Übergabe per const-Value zu verhunzen ist erst recht usnchön.
- vorzeitige optimierungen sind auch unschön. und sich beim tippen Gedanken zu machen über die kopie eines 1-byte-objekts vs eine 4-byte-referenz (viel mehr wirds kaum sein) IST vorzeitig.
- Einfache Konventionen die jeder kennt und versteht sind dagegen sehr schön.
Und Konvention ist, einfach alles was kein eingebauter Datentyp ist, per const-referenz zu übergeben. WENN die Referenz teurer ist als eine Kopie, und WENN der Compiler sich da so schon nicht eigenständig drum kümmert (bei ner 1-char-klasse wird vermutlich jede methode kurz und inline sein udn am ende die ganze klasse wegoptimiert und ein nackter char bleibt übrig...), und WENN dir dann auch noch ein Profiler sagt, dass genau diese Übergabe eine wirklich kritische Stelle ist wo dir viel Performance verloren geht...
Dann und nur dann ists an der Zeit, sich um den Spezialfall Gedanken zu machen - und vermutlich rettet dann ein Neudesign eher den Tag als sich um const value oder dergleichen Gedanken zu machen.
-
Den Spezialfall verstehe ich nicht so ganz. const-Memberfunktionen können wunderbar auch mit nicht-konstanten Objekten direkt aufgerufen werden, es sei denn, es ist ein nicht-konstanter Overload im Weg. Das wäre aber nur tragisch, falls es signifikante Unterschiede zwischen beiden gibt (man denke z.B. an std::string - dort ist das Kopieren aber selbst mit COW nicht ganz billig).
Zudem ist, wie richtig gesagt wurde, top-level const-Qualifikation von Funktionsparametern irrelevant für den Aufrufer. Wichtig dabei ist aber, dass diese Qualifikation nicht einmal in den Funktionstyp miteingeht. Es ist mithin möglich und durchaus sinnvoll, diese Qualifikation nur bei der Implementation anzugeben - der Aufrufer bekommt davon nichts mit.
Und selbst wenn man auf diese andere Deklaration bei der Funktionsdefinition verzichten will (die ggf. manche Tools durcheinanderbringen könnte), gibt es eine einfache Lösung: indem wir in der Funktion einfach eine const-Referenz auf den Parameter definieren und anschließend nur diese nutzen.void f(Foo /* should be const */ x) { const Foo& const_x = x; // benutze const_x ...
-
Werner Salomon schrieb:
Doch das geht, da das const nur bedeutet, dass die Liste nicht verändert wird. Das Objekt Vektor liegt als Kopie vor und die Kopie könnte man ändern.
Aber warum willst Du denn den X-Wert überschreiben, den Du gerade zusammen mit dem Vektor aus der Liste geholt hast? das macht doch keinen Sinn - oder? Schau Dir mal zu dem Thema Set-Methoden diesen Thread an. Jester und ich sind da verschiedener Meinung - also mache Dir selber ein Bild.Zum Thema der mathematische Vektor als Klasse findest Du hier einiges.
Gruß
WernerWieso liegt eine Kopie vor? Ich übergebe bzw. Kopiere höchstens den Zeiger auf den Vektor, von daher macht es auch Sinn den Vektor zu ändern.
Ein Beispiel:
Die Vektorklasse bildet die Oberklasse für ein grafisches Objekt, was sich auf dem Bildschirm bewegt. Das macht ja erstmal Sinn, da sich eigentlich jedes (bewegliche) Objekt auf dem Bildschirm mathematisch als Vektor darstellen lässt. Die Unterklasse implementiert dann weitere Funktionen wie z.B. das genau Aussehen des Objektes (z.B. ein Auto).
Wenn das Auto nun an den Rand kommt, soll es an der anderen Seite wieder auftauchen und dafür muss es dem Programm doch möglich sein die Position des Vektors (und damit des Autos) zu ändern.
Nun haben wir viele Autos auf dem Bildschirm und sammeln die in einer Liste und schon sind wir bei dem dargestellten Beispiel.Wie ihr in dem anderen Thread besprochen habt, es ist hier wirklich eine design Frage.
Also halten wir fest:
- Es macht kein Sinn Standardtypen (wie char, int, bool) als Parameter eine Funktion konstant zu machen
- Call by Reference immer mit const, es sei denn ich will die Variable auf die gezeigt wird wirklich ändernBleibt für mich gerade nur noch die Frage wann ich eine Funktion konstant (nennt man das dann so?), also "const" am Ende der Funktion schreibe.
Hat dieses const wirklich nur den Sinn zu sagen, dass die Übergeben Variable nicht geändert wird?Also:
void machwas(Vector &pVector) const { pVector = NULL; //Geht nicht pVector.SetXKoord(1000); //keine Ahnung; Wann darf man dies und wann nicht? double test = pVector.GetXKoord(); //geht }
-
Bleibt für mich gerade nur noch die Frage wann ich eine Funktion konstant (nennt man das dann so?), also "const" am Ende der Funktion schreibe.
Hat dieses const wirklich nur den Sinn zu sagen, dass die Übergeben Variable nicht geändert wird?Nein. Es bedeutet, dass Du die Elemente der Klasse zu der die Funktion gehört nicht ändern darfst.
-
pumuckl@logged_off schrieb:
const value: eigentlich würden nach der Liste hier nurnoch kleine Objekte (eingebaute Typen) landen, die du in der Funktion nicht verändern willst. Das ist aber nur eine Einschränkung an dich als Implementor der Funktion, nicht an den Aufrufer. Allgemein stellt eine Funktionsschnittstelle aber nur Garantien und Anforderungen an den Aufrufer dar, keine Implementationsdetails. const value macht man also allgemein nicht.
Dazu habe ich eine Frage. Angenommen du hast eine relativ komplexe seter Funktion die auch was ausrechen muss.
void setBla(const int i) { for(int x = 0; x < i; i++) { calc(x, i); // rechne irgendwas abhängig von i und x } }Ich setzte hier i immer auf const um sicher zu gehen das der Compiler besser optimieren kann. Würde man beim for noch ein
[ccp]
#pragma omp parallel for
[/cpp]
hinzufügen wüsste ich nicht mehr was passiert...Kann es also nicht sein dass das const aus Optimierungsgrüden doch sinnvoll ist?
Ich pers. versuch in sowas so penibel wie möglich zu sein und setzte jede variable auf const die es auch ist. Schaden tut es nicht und wenn man es sich angewöhnt hat geht es wie von selbst.LG
Neph
-
camper schrieb:
Zudem ist, wie richtig gesagt wurde, top-level const-Qualifikation von Funktionsparametern irrelevant für den Aufrufer. Wichtig dabei ist aber, dass diese Qualifikation nicht einmal in den Funktionstyp miteingeht. Es ist mithin möglich und durchaus sinnvoll, diese Qualifikation nur bei der Implementation anzugeben - der Aufrufer bekommt davon nichts mit.
Hast du das gelesen und verstanden?
EDIT: Die Frage geht an den Threadersteller nicht an camper.
-
Pille456 schrieb:
Werner Salomon schrieb:
Doch das geht, da das const nur bedeutet, dass die Liste nicht verändert wird. Das Objekt Vektor liegt als Kopie vor ...
Wieso liegt eine Kopie vor? Ich übergebe bzw. Kopiere höchstens den Zeiger auf den Vektor, von daher macht es auch Sinn den Vektor zu ändern.
Nun - weil Du es so hingeschrieben hast:
Pille456 schrieb:
Andere Beispielklasse:
class List { // ... public: Vector GetVector();.. bedeutet ein Objekt der Klasse Vector zurückzugeben. Sonst hättest Du
class List { // ... public: Vector* GetVector();schreiben müssen. Dies gibt einen Pointer auf einen Vector zurück.
Pille456 schrieb:
Ein Beispiel:
Die Vektorklasse bildet die Oberklasse für ein grafisches Objekt, was sich auf dem Bildschirm bewegt. Das macht ja erstmal Sinn, da sich eigentlich jedes (bewegliche) Objekt auf dem Bildschirm mathematisch als Vektor darstellen lässt. Die Unterklasse implementiert dann weitere Funktionen wie z.B. das genau Aussehen des Objektes (z.B. ein Auto)... vielleicht. Man könnte aber auch sagen, dass ein Auto eine Position hat - also eine Klasse Auto einen Member vom Typ Vector besitzt.
Pille456 schrieb:
Wenn das Auto nun an den Rand kommt, soll es an der anderen Seite wieder auftauchen und dafür muss es dem Programm doch möglich sein die Position des Vektors (und damit des Autos) zu ändern.
Die Position ja - aber nicht zwingend die Koordinaten einzeln. Man könnte sich ja vorstellen, dass die Y-Position mit geändert wird, so dass sich das Auto wie auf einer Spirale bewegt.
Meines Erachtens ist es für den allgemeine Fall sauberer die neue Position einfach zuzuweisen - alaVector aktPosVonAuto; aktPosAuto = Vector( neuesX, aktPosVonAuto.GetYKoord() );.. aber ich möchte nicht diese Diskussion über Set-Methoden wiederholen.
Pille456 schrieb:
Also halten wir fest:
- Es macht kein Sinn Standardtypen (wie char, int, bool) als Parameter eine Funktion konstant zu machen
- Call by Reference immer mit const, es sei denn ich will die Variable auf die gezeigt wird wirklich ändernso ist es.
Pille456 schrieb:
Bleibt für mich gerade nur noch die Frage wann ich eine Funktion konstant (nennt man das dann so?), also "const" am Ende der Funktion schreibe.
immer genau dann, wenn die Methode das Objekt, bei dem die Methode aufgerufen wird, nicht verändert.
Gruß
Werner
-
-
Nephelauxetic schrieb:
Kann es also nicht sein dass das const aus Optimierungsgrüden doch sinnvoll ist?
Herb Sutter schrieb:
Premature optimization is evil.
Lass den Compiler seine Arbeit machen, mach du deine
Vor allem versuch nicht, den Compiler bei seinen Optimierungen zu unterstützen, das kann gut nach hinten losgehen. Der versteht sein Geschäft nämlich meist besser als du 
-
Lass den Compiler seine Arbeit machen, mach du deine
Vor allem versuch nicht, den Compiler bei seinen Optimierungen zu unterstützen, das kann gut nach hinten losgehen. Der versteht sein Geschäft nämlich meist besser als du 
Allgemein wuerd ich dir da zustimmen. In diesem konkreten Fall sehe ich aber keine Moeglichkeit wie das nach hinten losgehen koennte.
-
Don06 schrieb:
camper schrieb:
Zudem ist, wie richtig gesagt wurde, top-level const-Qualifikation von Funktionsparametern irrelevant für den Aufrufer. Wichtig dabei ist aber, dass diese Qualifikation nicht einmal in den Funktionstyp miteingeht. Es ist mithin möglich und durchaus sinnvoll, diese Qualifikation nur bei der Implementation anzugeben - der Aufrufer bekommt davon nichts mit.
Hast du das gelesen und verstanden?
EDIT: Die Frage geht an den Threadersteller nicht an camper.
Ich weiß, ich wiederhole mich. Aber hast du das verstanden, was camper da gesagt hat? Ich fasse es noch mal in meinen Worten zusammen.
void foo(int i); void foo(const int i);Das ist _keine_ Überladung! Es sind Deklarationen von ein und derselben Funktion. In einer Deklaration schließt man meist so eine Art "Vertrag" mit dem Aufrufer, was für Bedingungen die Parameter erfüllen müssen und welchen Einfluss die Funktion auf die Parameter nimmt. Die const-Qualifikation von value-Parametern gibt weder dem Aufrufer noch der Funktion irgendwelche Informationen über diesen "Vertrag". Es handelt sich ja schließlich nur um eine lokale Variable in die kopiert wird. Teilweise kann so eine const-Qualifikation in der Deklaration zu Verwirrung beim Aufrufer führen. Er denkt vielleicht, das const bringt im irgendwelche Informationen über die Arbeitsweise der Funktion. Das ist aber ein Fehleinschätzung.
Ungeachtet dessen kann es durchaus Sinn machen, Parameter, die innerhalb der Funktion nicht geändert werden sollen, const zu machen. Dies sollte aber aus genannten Gründen dann nur in der Definition erfolgen.
z.B.
// foo.h void foo(int i); // foo.cpp void foo(const int i) { // i kann nicht geändert werden }Gruß
Don06