const Durcheinander in einer Klasse bzw. Deklaration
-
Hi,
nur noch eine kleine Randbemerkung: Hier braucht man keine "class"-Klassifizierer in C++ - und ich finde sie auch unschön/verwirrend:
Winn schrieb:
... foo(const class foo &moo) ... void operator=(class foo &moo) ... int main (int argc, char * const argv[]) { class foo moep; ...=>
... foo(const foo &moo) ... void operator=(foo &moo) ... int main (int argc, char * const argv[]) { foo moep; ...schließlich schreibst Du ja auch nicht
class std::string sFoo;, oder ? ;))Gruß,
Simon2.
-
Simon2 schrieb:
schließlich schreibst Du ja auch nicht
class std::string sFoo;, oder ? ;))
Stimmt, sowas mach ich nicht

Insgesamt kenn ich vier Arten "const" zu verpacken, davon verstehe ich die ersten Zwei, wahrscheinlich
Kann mir jemand diese Funktions Deklarationen erklären ?double *getFoo(const double *foo)Konstanter double Pointer als Argument
const double *getFoo(double *foo)Konstanter double Pointer als Rückgabe
double *getFoo(double * const foo)Ist es das gleiche wie das Erste ?
double *getFoo(double *foo) constNix kapische

-
Der dritte ist ein konstanter Zeiger auf eine (veränderliche) Variable (siehe der Thread von BlueSteel, das vierte kennzeichnet das Objekt hinter dem this-Zeiger als konstant.
(die ersten beiden verwenden einen Zeiger auf eine konstante "Variable" als Parameter bzw. Rückgabetyp)
-
Hi,
eigentlich ist es nicht schwierig: const macht immer das Ding const, HINTER dem es steht:
double const: ein unveränderliches doubleconst double: ist identisch mitdouble const; hat man aus historischen Gründen zugelassen (ist leider die weitverbreitetste Form, obwohl sie eigentlich die "Ausnahme von der Regel" ist); das geht NUR, wenn vor const nichts mehr steht.double const *: ein (veränderbarer) Zeiger auf ein (unveränderbares) double; Hier darf also der Zeiger woanders hinzeigen, aber das, wohin er zeigt, darf nicht verändert werden.const double *identisch mitdouble const *;double * const: ein (unveränderbarer) Zeiger auf ein (veränderbares) double; Hier darf also der Zeiger NICHT woanders hinzeigen, aber das, wohin er zeigt, darf verändert werden.double const * const: ein (unveränderbarer) Zeiger auf ein (unveränderbares) doublevoid f() const: eine konstante Memberfunktion; in dieser Funktion darf der Wert des Objektes zu dem sie gehört (="this"), nicht verändert werden.
Gruß,
Simon2.
-
Du rufst doch eine Methode immer mit einem Objekt auf. Dieses Objekt ist in der Methode (auch implizit) als *this bekannt. Auch this kann einen const-Qualifizierer haben, das ist wichtig für das letzte Beispiel:
Winn schrieb:
Insgesamt kenn ich vier Arten "const" zu verpacken, davon verstehe ich die ersten Zwei, wahrscheinlich
Kann mir jemand diese Funktions Deklarationen erklären ?double *getFoo(const double *foo)Konstanter double Pointer als Argument
Pointer auf konstanten double-Wert. Der Pointer "foo" darf innerhalb der Funktion verbogen werden, sprich auf ein anderes konstantes double zeigen. Aber den double-Wert (das Zeigerziel oder "Pointee") darfst Du nicht verändern.
const double *getFoo(double *foo)Konstanter double Pointer als Rückgabe
Wieder: Pointer auf konstanten double-Wert. Du darfst das Ziel ändern, aber nicht den Wert, der dort steht.
double *getFoo(double * const foo)Ist es das gleiche wie das Erste ?
Nein, hier hast Du "konstanter double Pointer". Also ein konstanter Zeiger auf einen double-Wert. Der Zeiger darf nicht verbogen werden, jedoch darfst Du den double-Wert verändert.
double *getFoo(double *foo) constNix kapische

Hier kommt o.a. Satz zum Einsatz: Das const hier bedeutet, das *this konstant ist. Das heisst innerhalb dieser Methode darfst Du keine Membervariablen verändern. Du darfst auch keine weitere Methode aufrufen, es seidenn diese ist ebenfalls const.
-
-
Undertaker schrieb:
http://c2.com/cgi/wiki?AvoidConstCompletely
http://c2.com/cgi/wiki?ConstIsaVirus

Könntest du bitte deine unqualifizierten Bemerkungen sein lassen - danke.
(und nach solchen Einwürfen fragst du dann noch, was ich eigentlich gegen dich habe)
-
Danke wegen dieser umfassenden Übersicht !! Dabei würde ich gerne noch die Einsatzgebiete etwas abstecken, sonst setzt sich das nicht in meinen grauen Zellen :p
double const: ein unveränderliches doubleconst double: ist identisch mitdouble const; hat man aus historischen Gründen zugelassen (ist leider die weitverbreitetste Form, obwohl sie eigentlich die "Ausnahme von der Regel" ist); das geht NUR, wenn vor const nichts mehr steht.
Würde ich verwenden, wenn ich mathematische/physikalische Konstanten wie z.B. PI benötigen würde.
double const *: ein (veränderbarer) Zeiger auf ein (unveränderbares) double; Hier darf also der Zeiger woanders hinzeigen, aber das, wohin er zeigt, darf nicht verändert werden.const double *identisch mitdouble const *;
Würde ich nun verwenden, wenn ich eine mathematische Funktion hätte, welche mehrere mathematischen/physikalischen Konstanten benötigt. Nun könnte ich mit einem Pointer die math./phy. Konstanten nutzen.
double * const: ein (unveränderbarer) Zeiger auf ein (veränderbares) double; Hier darf also der Zeiger NICHT woanders hinzeigen, aber das, wohin er zeigt, darf verändert werden.
Würde ich nun verwenden, um in einem veränderbaren double Array, Hilfsklassen bzw. -funktionen zu definieren, welche stetig nur auf einen fest definierten Bereich anzuwenden sind.
double const * const: ein (unveränderbarer) Zeiger auf ein (unveränderbares) double
Hmm... wenn ich den obengenannten Hilfsfunktionen auf eine math./phys. Konstante zu greifen müßte.
void f() const: eine konstante Memberfunktion; in dieser Funktion darf der Wert des Objektes zu dem sie gehört (="this"), nicht verändert werden.
Würde ich nun etwas umformulieren in, eine konstante Memberfunktion dessen Rückgabewert, "welche bei einem Aufruf in einer anderen Routine", in diesem Aufruf nicht verändert werden darf. Stimmt das ?
Kann jemand pauschal und aus dem Bauch heraus sagen, welche Routine schneller ist, ich meine Referenzen sind ja schonmal schneller, weil keine Kopie erstellt wird und wenn ich nun konstante Referenzen habe, könnte ich mir vorstellen, daß auch hier weniger Oerhead entsteht, also auch wieder schneller wird ?
-
CStoll schrieb:
Undertaker schrieb:
http://c2.com/cgi/wiki?AvoidConstCompletely
http://c2.com/cgi/wiki?ConstIsaVirus

Könntest du bitte deine unqualifizierten Bemerkungen sein lassen
ich denke, es könnte hilfreich sein, wenn der OP 'const' auch aus diesem blickwinkel betrachtet.

-
Zeiger auf konstante double's habe ich bisher eher selten verwendet, der Rest könnte hinkommen (wobei, anstelle eines konstanten Zeigers verwendet man in C++ oft Referenzen (und std::vector anstelle von Arrays)).
Aber: const-Methoden (void f() const;) sind durchaus nützlich - wenn du irgendwo ein konstantes Objekt übergibst, wird sich der Compiler weigern, dessen "normale" Methoden zu verwenden:
class test { public: void f1(); void f2() const; }; void test_fn(const test* param) { param->f1();//Fehler param->f2();//funktioniert }@Performance: const-Referenzen gegen Kopien haben den Geschwindigkeitsvorteil, weil nichts kopiert werden muß (kann bei umfangreichen Klassen teuer werden), bei allen anderen Konstellationen ist es eher eine Frage der Semantik, was "richtig" ist.
-
Hi Winn,
noch eine kleine "Verständniserleichterung": "const" hat nichts mit "Konstante" zu tun, sondern ist eine "Schnittstellenzusage" !
Wenn ich eine Variable als const deklariere, bedeutet das nicht automatisch, dass sich dahinter eine "Konstante" verbergen muss, sondern nur, dass ich verspreche, den Wert über diese Variable nicht zu verändern !Mal ein Beispiel (mit "Referenzen" statt Pointern, weil ich die lieber mag
):void f_const(int const& i) { cout << i; // operator<<(int const, ostream&) verändert i nicht) } void f_nonconst(int & i) { cout << ++i ; // operator++() verändert i } void f_beides(int const& i1, int& i2) { cout << i1 << ++i2; cout << i1 << i2; } int main() { // einmal mit non-const-variable int i1 = 2; f_const(i1); // OK "obwohl i1 non-const" f_nonconst(i1); // OK; geht natürlich auch // einmal mit const-variable int const i2 = 3; f_const(i2); // OK f_nonconst(i2); // geht NICHT !! => Compiler-Error // einmal mit const-Referenz int const& i3 = i1; // OK: Ich sage nur: Über i3 will ich nicht verändern f_const(i3); // OK ++i1; // jetzt hat sich der Wert, auf den i3 verweist verändert - aber eben nicht über i3 f_const(i3); // OK f_nonconst(i3); // => Compiler-Error // jetzt mal ein fieses Beispiel - dem geneigten Leser zum Selbstdurchdenken überlassen: f_beides(i1, i1); return 0; }Gruß,
Simon2.
-
Undertaker schrieb:
...
ich denke, es könnte hilfreich sein, wenn der OP 'const' auch aus diesem blickwinkel betrachtet.

So wie der eine Blinde den anderen führt ? Interessanter Ansatz...

-
Simon2 schrieb:
void f_beides(int const& i1, int& i2) { cout << i1 << ++i2; cout << i1 << i2; } int main() { int i1 = 2; int const i2 = 3; int const& i3 = i1; f_beides(i1, i1); return 0; }Ich würde nun sagen, daß der Compiler meckert, i1 ist non-const und wird über das erste Argument der Funktion abgelehnt. Interessant würde es werden, wenn man "f_beides(i3,i1)" macht. Zur Runtime würde wahrscheinlich erst i1 auf 3 inkrementiert, anschliessend cout ausgeführt, also es würde "33" ausgegeben und danach auch wieder "33" ...
-
Nein, das compiliert anstandslos - du kannst eine nicht-konstante Variable problemlos an eine const-Referenz übergeben und du kannst genauso problemlos mehrere Referenzen auf die selbe Variable anlegen (und parallel verwenden). Allerdings trittst du dir damit selber auf die Füße, weil du nicht mehr vorhersagen kannst, was die erste Ausgabe-Anweisung liefern wird (++i2 liefert auf jeden Fall "3" zurück, aber ob das Inkrement durchgeführt wird, bevor oder nachdem i1 abgefragt wurde, liegt im Ermessen des Compilers).
-
CStoll schrieb:
Nein, das compiliert anstandslos - du kannst eine nicht-konstante Variable problemlos an eine const-Referenz übergeben und du kannst genauso problemlos mehrere Referenzen auf die selbe Variable anlegen (und parallel verwenden). Allerdings trittst du dir damit selber auf die Füße, weil du nicht mehr vorhersagen kannst, was die erste Ausgabe-Anweisung liefern wird (++i2 liefert auf jeden Fall "3" zurück, aber ob das Inkrement durchgeführt wird, bevor oder nachdem i1 abgefragt wurde, liegt im Ermessen des Compilers).
"auf jeden Fall" ist eine gewagte Aussage. Das Ganze ist schlicht undefiniert und kann sich je nach Optimierungsstufe und Mondphase völlig anders verhalten - man denke z.B. an weitergehende Optimierungen durch inlining. Hier zu behaupten, der Compiler hätte hier nur eine bestimmte Wahlmöglichkeit, führt nur dazu, das Leute nur einmal ausprobieren, wie sich ihr Compiler verhält und dann ihren Code entsprechend schreiben.
const als solches (hieß ursprünglich mal readonly, was weniger irreführend ist) ist eine Bedingung an den zu deklarierenden Bezeichner, nicht zwingend das Objekt, an dass dieser Bezeichner gebunden wird. Aus diesem Grunde ist es möglich, eine Referenz-auf-const an ein nicht-konstantes Objekt zu binden, denn dieses Const impliziert nur, dass das Objekt nicht mittels dieser Referenz verändert wird. Umgekehrt ist es nicht möglich, eine Referenz-auf-nicht-const an ein konstantes Objekt (oder ein Objekt, dass konstant erscheint, weil über eine Referenz-auf-const darauf verwiesen wird) zu binden.
-
Schön gesagt, camper
(ich komme immer nicht auf Ausdrücke wie "Bezeichner" oder "gebunden an ...")
CStoll schrieb:
Nein, das compiliert anstandslos ...Allerdings trittst du dir damit selber auf die Füße, weil du nicht mehr vorhersagen kannst, was die erste Ausgabe-Anweisung liefern wird ...
CStoll schrieb:
...Das Ganze ist schlicht undefiniert ..
Darauf wollte ich hinaus.
Mir ist erst durch diesen Thread aufgefallen, wie leicht man hier einen blöden Fehler einbauen kann...
(Obwohl meine Kollegin meinte:" Soo einfach kann es ja nicht sein, denn Dir ist das doch noch nie passiert, oder "
)Gruß,
Simon2.
-
Einen informativen Beitrag zur const-correctness hat auch Herb Sutter geliefert:
http://www.gotw.ca/gotw/006.htm