namespace cpp

C++ lernen, kennen, anwenden

Benutzer-Werkzeuge

Webseiten-Werkzeuge


lernen:const

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

lernen:const [2014-07-13 16:14] (aktuell)
Zeile 1: Zeile 1:
 +====== Political const correctness ======
 +
 +> Der Hauptzweck der ''​DATA''​-Anweisung ist es, Namen für Konstanten festzulegen;​ anstatt an jeder Stelle im Programm, wo π vorkommt, ''​3.141592653589793''​ zu schreiben, kann man diesen Wert mit einer ''​DATA''​-Anweisung einer Variablen ''​PI''​ zuweisen und diese dann anstelle der Langform verwenden. Dies vereinfacht auch die Änderung des Programms, falls sich der Wert von π ändern sollte.
 +>>​--- ​   FORTRAN Manual der Firma Xerox
 +
 +>    All this provides a significant additional form of type checking and safety in your programming. The use of so-called const correctness (the use of const anywhere you possibly can) can be a lifesaver for projects. Although you can ignore const and continue to use old C coding practices, it's there to help you.
 +>>​--- ​   Bruce Eckel
 +
 +>    It's hard at first, but using const really tightens up your coding style.
 +>    Const correctness grows on you.
 +>>​--- ​   Todd Hoff
 +
 +Die Konstant-Deklaration ist von politischer Tragweite. ​
 +Ihr korrekter Einsatz hilft, ​
 +saubere Entwürfe zu gestalten und ihre Datenintegrität zu sichern. ​
 +Dieser Artikel erörtert das Warum und Wie für den Einsatz des Schlüsselwortes ​
 +''​const''​ in C++ in prozeduralen und objekt-orientierten Programmen.
 +
 +===== Ode an ein Schlüsselwort =====
 +
 +In Abwandlung eines Songs von Joan Baez: 
 +  const is just a five letter word. 
 +  ​
 +Na und? Mir geht es mit dem Wort wie einem Alkoholiker mit dem Suchtmittel: ​
 +ich habe kein Problem damit, sondern ohne. Ich will zu erklären versuchen, warum. ​
 +Ich hoffe, nachher geht es Ihnen wie mir. 
 +Seien Sie nicht konstant, verändern Sie sich! 
 +Es wird künftigen Quelltexten gut tun.
 +
 +==== C lernt von C++ ====
 +
 +Nein, Sie haben sich nicht verlesen. ​
 +Das Schlüsselwort ''​const''​ wurde 1983 in C++ und 1984 
 +in den Standard der Vorgängersprache C übernommen [D&​E]. ​
 +Eingeführt wurde es, um unabsichtliche Änderungen von Werten zu erschweren. ​
 +Des Öfteren kommen Zahlen im Quelltext vor, 
 +die man besser an einer Stelle definiert, ​
 +weil sie im Programm zwar als unveränderlich betrachtet werden, ​
 +aber sich von Version zu Version auch mal ändern können:
 +
 +<code cpp>
 +const int ARRAYSIZE = 10;
 +const double PI = 4.0*atan(1.0);​
 +</​code>​
 +(siehe obiges Xerox-Zitat [PI]). ​
 +Solche globalen "​magischen"​ Zahlen im Quelltext zu suchen und zu übersehen, ​
 +ist ein Alptraum jedes Wartungsprogrammierers. ​
 +C-Makrokonstanten in C++ zu verwenden ist zwar möglich, ​
 +aber es gibt Bestrebungen,​ dies künftig zu ächten:
 +
 +<code cpp>
 +#define ARRAYSIZE 10
 +#define PI (4.0*atan(1.0)) ​   /* wird jedes Mal neu berechnet ! */
 +</​code>​
 +
 +Dankbare Abnehmer für Konstanten sind Felder:
 +<code cpp>
 +  float array[ARRAYSIZE];​
 +
 +  std::cout << "​Eingabe von " << ARRAYSIZE << " Werten:​\n";​
 +  for (int i = 0; i < ARRAYSIZE; i++)
 +  {
 +    std::cout << "Wert " << (i+1) << " : "; ​
 +    std::cin >> array[i];
 +  }
 +  float sum = 0;
 +  for (int i = 0; i < ARRAYSIZE; i++)
 +  {
 +    sum += array[i];
 +    array[i] = sum;
 +  }
 +  for (int i = 0; i < ARRAYSIZE; i++)
 +  {
 +    std::cout << "​Partialsumme " << (i+1) << " = " << array[i] << '​\n';​
 +  }
 +</​code>​
 +
 +Bisher mussten in C und müssen in C++ die Feldgrößen konstante, ​
 +beim Übersetzen festliegende Ausdrücke sein. Künftige C++-Standards können ​
 +evtl. die Festlegungen des neuen C-Sprachstandards ISO 9899:1999 übernehmen ​
 +und zur Laufzeit variabel festlegbare Feldgrößen erlauben ​
 +(wie g++ dies schon tut). 
 +Was wird dann wohl mit dem Schleifenende,​ wenn sich der Wert ''​ARRAYSIZE'' ​
 +zwischendurch auch, natürlich unabsichtlich,​ ändern kann?
 +
 +==== Konstante Variablen? ====
 +
 +Variablen sind Speicherzellen, ​
 +deren Wert sich durch Zuweisungen oder Operationen ändern kann. 
 +Konstanten ändern sich nicht:
 +
 +<code cpp>
 +int n = 10;           // Variablendefinition
 +const int SIZE = 10;  // const Variable
 +</​code>​
 +
 +Der direkte Änderungsversuch erzeugt einen Übersetzungsfehler. ​
 +C-Programmierer sagen trotzdem zur ''​const''​-Variablen ungern Konstante, weil sie
 +
 +  *  wissen, wie man const umgehen kann,
 +  *  der Drang, das zu tun, beliebig groß werden kann und
 +  *  die Bezeichnung Konstante aus historischen Gründen vom Präprozessor belegt ist.
 +
 +(Ich werde schlechten Stil an dieser Stelle nicht unterstützen und nicht verraten, ​
 +wie ''​const''​ zu umgehen ist. 
 +Die kriminelle Energie, das herauszufinden,​ muss schon jeder selbst aufbringen.)
 +
 +Aber ist das nicht ein Widerspruch in sich: konstante Variable? ​
 +Das Schlüsselwort ''​const''​ meint, das der Programmierer den Wert nicht ändern will. 
 +Sehr wohl kann sich der Wert einer ''​const''​-Variable aber aus Gründen ändern, ​
 +die der Programmierer nicht beeinflussen kann, 
 +z.B. wenn die am seriellen Port angeschlossene Maus bewegt wird:
 +<code cpp>
 +const volatile char* COM1port = 0x000003f8; // serial in port (DOS)
 +</​code>​
 +
 +Der Port wird als flatterhaft (flüchtig = volatil) gekennzeichnet, ​
 +damit der Compiler tatsächlich jedesmal den Inhalt abfragt. ​
 +Compiler sind nämlich auch faul und können manche Zugriffe wegoptimieren, ​
 +besonders wenn sie durch Compiler-Einstellungen (Optimierung) dazu aufgefordert werden.
 +
 +==== Konstant gute Referenzen ====
 +
 +Manche Objekte von zusammengesetzten (nutzerdefinierten) Datentypen ​
 +sind echte Speicherfresser. Ihre Übergabe als Wertparameter kann sehr teuer werden:
 +
 +<code cpp>
 +struct Vec3 { double x, y, z; };
 + 
 +void ausgabe(Vec3 v); // muss 3 x 8 = 24 Byte kopieren !!
 +</​code>​
 +
 +Aus diesem Grund wurden extra für C++ die Referenzen erfunden, ​
 +die es ermöglichen,​ (verdeckt) nur die Adresse einer Variable zu übergeben:
 +
 +<code cpp>
 +void ausgabe2(Vec3&​ v);  // nur 4 Byte Adresse
 +</​code>​
 +
 +Nur haben Referenzparameter wie die (in C üblicherweise) ​
 +mittels Zeiger übergebenen Parameter die unangenehme Eigenschaft, ​
 +sich auch innerhalb der Prozedur ändern zu können. ​
 +Diese Änderung wirkt dann zurück ins Hauptprogramm, ​
 +wie in alten FORTRAN-Zeiten. Um entscheiden zu können, ob
 +
 +<code cpp>
 +  Vec3 v = { 1, 2, 3 }; 
 +  ausgabe2(v);​
 +  // immer noch (1,2,3) ?
 +</​code>​
 +
 +den Wert von ''​v''​ ändert, ​
 +müsste man den Quelltext von ''​ausgabe2()''​ kontrollieren,​ und zwar jedesmal, ​
 +wenn man wieder neu übersetzt; ​
 +schließlich könnte einer inzwischen den Code von ''​ausgabe2()''​ geändert haben. ​
 +Dies ist nicht praktikabel,​ abgesehen davon, ​
 +dass Quelltext von Bibliotheken dem Nutzer auch als Geschäftsgeheimnis ​
 +vorenthalten werden kann und wird. 
 +Bei der folgenden Funktion darf der Nutzer darauf vertrauen, ​
 +dass der übergebene Wert nicht geändert wird:
 +
 +<code cpp>
 +void ausgabe3(const Vec3& v); // freiwillige Selbstverpflichtung
 +</​code>​
 +
 +Der Implementierer von ''​ausgabe3()''​ gibt bekannt, dass er ''​v''​ nicht ändern will. 
 +Eine Analogie zur Dateiorganisation des Betriebssystems ​
 +ist an der Stelle vielleicht nützlich: ​
 +''​const''​ wirkt wie das Schreibschutzattribut (oder entzogenes Schreibrecht) ​
 +für den Nutzer der ''​const''​-Variablen. ​
 +Referenzen entsprechen Verküpfungen (oder soft links) auf anderswo stehende Daten.
 +
 +Ändert der Implementierer das konstante ''​v''​ unabsichtlich, ​
 +wird er durch eine Fehlermeldung des Compilers deutlich darauf hingewiesen. ​
 +Will er es absichtlich tun, 
 +muss er unweigerlich kriminell werden und die zugesicherte Schnittstelle verletzen, ​
 +z.B. durch einen Typecast. ​
 +Tut er es und liegt dann ''​v''​ beim Aufruf im Nur-Lese-Bereich des Programmspeichers, ​
 +riskiert der Anwender den Abschuss des Programms durch das stets wachende Betriebssystem. ​
 +Dieses ---- ich meine nicht die DOSe und deren graphische Oberfläche --- 
 +versteht an dieser Stelle keinerlei Spaß:
 +
 +  segmentation fault, core dump. 
 +
 +Deutsche Fensterer kennen (und lieben) die allgemeine Schutzverletzung. ​
 +Manchmal ist Kontrolle eben besser. (Nur: Wozu ist dann Vertrauen gut?) 
 +Bei solchen Betrachtungen darf man schon mal ethischen bis religiösen Eifer zeigen: ​
 +Programme in einem Multi-Tasking-System müssen sich wie Menschen auch 
 +an soziale Regeln halten, soll das Ganze funktionieren. ​
 +Und leider kann man sich nicht immer darauf verlassen. ​
 +Die Kontrolle ihrerseits muss bezahlt werden, mit Zeit, Geld und Resourcen.
 +
 +==== Konstanten und Zeiger ====
 +
 +Eine schwierige Frage: Was bedeuten
 +
 +<code cpp>
 +char*
 +const char*
 +char* const
 +const char* const
 +</​code>​
 +
 +Eine einfachere Frage: ​
 +Was ist (vom Namen abgesehen) der Unterschied der nächsten Zeilen?
 +
 +<code cpp>
 +  char s1[] = "​Hallo";​
 +  char *s2  = "​Ballo";​
 +</​code>​
 +
 +Antwort: Der Zeiger ''​s2''​ darf versetzt werden, der Feldname ''​s1''​ dagegen nicht.
 +
 +<code cpp>
 +  s2 = s1; // erlaubt
 +  s1 = s2; // Fehler! Ein Feld kann nicht umziehen!
 +</​code>​
 +
 +Feldnamen können wie unveränderliche Zeiger betrachtet werden ​
 +(obwohl sie es genau genommen auch nicht sind). ​
 +Die Anfangswertbelegung von ''​s2''​ ist darüber hinaus auch noch gefährlich:​
 +
 +<code cpp>
 +  s2[1] = '​e';​
 +</​code>​
 +
 +führt zu einem Programmabsturz (segmentation fault) unter Unix, 
 +sofern ''​s2''​ auf die Zeichenkette "​Ballo"​ im Nur-Lese-Programmspeicherbereich zeigt. ​
 +Der Inhalt von "​Hallo"​ dagegen darf geändert werden.
 +
 +Mit diesen Vor-Überlegungen kommen wir der Antwort auf die schwierige Frage näher. ​
 +Das Schlüsselwort ''​const''​ kann in (für Anfänger verwirrender) Kombination ​
 +mit Zeigern sogar mehrfach auftauchen:
 +
 +<code cpp>
 +        char *s3 = "​schlecht"; ​  // strcpy(s3, "​peng"​);​ => core dump!
 +  const char *s4 = "​sicher"; ​     ​
 +  char* const s5 = s1;           // zeigt immer auf den Feldanfang von s1 
 +  const char* const s6 = "​ewig";​
 +</​code>​
 +
 +Zum Verstehen hilft, die Definition "von innen nach außen"​ zu lesen, ​
 +d.h. vom definierten Namen ausgehend:
 +
 +  s6            const      *         ​char ​    const
 +  s6 ist ein konstanter Zeiger auf Zeichen-Konstanten
 +
 +mit dem unveränderlichen Inhalt "​ewig"​. ​
 +Durch das näherstehende ''​const''​ ist der Zeiger ''​s6''​ eingefroren. ​
 +Er darf weder woanders hinzeigen noch wandern, nicht mal wackeln. ​
 +Das weiter entfernte ''​const''​ verbietet außerdem die Änderung des Zeichenketteninhaltes.
 +
 +Die Konstant-Deklaration hat Folgen, ​
 +die den Umgang zunächst erschweren und dazu verführen, ​
 +''​const''​ generell wegzulassen. Der Compiler weigert sich bei dieser Anweisung:
 +
 +<code cpp>
 +  char* s7 = s4; // Fehler: kann const char* nicht in char* umwandeln
 +</​code>​
 +
 +Aus gutem Grund, denn nachfolgend könnte mit
 +
 +<code cpp>
 +  s7[0] = '​k';​
 +</​code>​
 +
 +sich jemand ins Fäustchen lachen und die (ersehnte) Schutzverletzung doch herbeiführen.
 +
 +===== const und OOP =====
 +
 +In der objektorientierten Programmierweise (OOP) nimmt die Bedeutung von 
 +''​const''​ weiter zu, 
 +da es hier sogar in noch mehr Zusammenhängen auftauchen kann und sollte:
 +
 +  *  nicht veränderbare Rückgabewerte von Methoden,
 +  *  nicht veränderbare Datenwerte in Objekten (konstante Attribute),
 +  *  nicht veränderbare Objekte und
 +  *  Methoden, die den Zustand von Objekten nicht ändern (wollen).
 +
 +Beim Erlernen einer neuen Technik macht man selten alles sogleich richtig. ​
 +Die objektorientierte Denkweise für sich genommen ist schon schwierig genug. ​
 +Die Syntax für die Klassen-Definition und für die -Implementation ist neu. 
 +Dabei kommt ein Aspekt wie der korrekte Einsatz des unscheinbaren Wörtchens ​
 +''​const''​ leicht unter die Räder. ​
 +Aus Fehlern lernt man (hoffentlich,​ falls überhaupt). ​
 +Dazu müssen wir erstmal welche machen.
 +
 +==== Die offene Hintertür ====
 +
 +Schaffen Sie auch Ihr Geld zur Bank? 
 +Eröffnen Sie ein Konto und lassen Sie Ihr mühsam Erspartes einschließen:​
 +
 +<code cpp>
 +class Konto
 +{
 +public:
 +  Konto(char inhabername[40],​ int anfangseinzahlung);​
 +  char* inhaber() { return name;  }
 +  int  guthaben() { return saldo; }
 +  // ...
 +private:
 +  int  saldo;
 +  char name[40]; ​
 +};
 +
 +Konto vielGeld("​du",​ 10000);
 +</​code>​
 +
 +Ist Ihr Geld sicher? An den Saldo kommt niemand heran. ​
 +Der liegt im privaten Bereich, auch der Inhabername ist privat. Wirklich? ​
 +Probieren wir es:
 +
 +<code cpp>
 +  strcpy(vielGeld.inhaber(),​ "​ich"​);​
 +  std::cout << vielGeld.inhaber() << '​\n';​ // Jetzt ist es meins!
 +</​code>​
 +
 +In der Klasse steht eine Hintertür offen. ​
 +Die Methode ''​inhaber()''​ liefert einen Zeiger ​
 +als Haken oder Diederich ins Innere ihres Geldtresors,​ der alles ändern darf. 
 +Ohne Fehlermeldung,​ ohne Hindernis. ​
 +Fünf unscheinbare Buchstaben am Ergebnistyp hätten uns aufgehalten:​
 +
 +<code cpp>
 +class Konto
 +{
 +public:
 +  const char* inhaber() { return name; }
 +  // ...
 +};
 + 
 + // ...
 + ​strcpy(vielGeld.inhaber(),​ "​ich"​);​ // Fehler! ​
 +</​code>​
 +
 +indem der Compiler beklagt, dass der von ''​inhaber()''​ gelieferte ​
 +''​const char*''​ nicht in ''​char*''​ umwandelbar ist, 
 +wie ''​strcpy(ziel,​ quelle)''​ das von der Zielzeichenkette erwartet.
 +
 +==== Konstante Attribute ====
 +
 +In den Geschäftsbedingungen der Bank sind Übereignungen möglicherweise ​
 +gar nicht vorgesehen. ​
 +Der Inhaber des Kontos sollte nicht wechseln dürfen, ​
 +vorbeugend gegen Geldwäsche und Spendenskandale. ​
 +Der Name des Inhabers wäre als konstant anzugeben:
 +
 +<code cpp>
 +class Konto
 +{
 +public:
 +  Konto(const char inhabername[40],​ int anfangseinzahlung)
 +  std::string inhaber() { return name; }
 +  // ...
 +private:
 +  const std::string name; 
 +  // ...
 +};
 +</​code>​
 +
 +Dann ist ein ''​std::​string''​ besser geeignet, denn wie sollte der Name 
 +mit ''​strcpy()''​ in ein konstantes ''​char''​-Feld geschrieben werden? ​
 +(Es geht, aber erfordert ''​const''​-Betrügereien.) ​
 +Konstante Attribute können nicht im Konstruktorrumpf zugewiesen werden. ​
 +Sie erhalten ihre Werte vor dem Betreten des Konstruktors ​
 +in der Initialisiererliste nach dem Doppelpunkt:​
 +
 +<code cpp>
 +Konto::​Konto(const char inhabername[40],​ int anfangseinzahlung)
 +: name(inhabername,​ 39), saldo(anfangseinzahlung)
 +{
 +}
 +</​code>​
 +
 +Das hindert gleichzeitig den Implementierer der Klasse daran, ​
 +in irgendeiner Methode (un)absichtliche Änderungen am Namen vorzunehmen.
 +
 +==== Konstante Feldparameter ====
 +
 +Der übernommene Inhabername wurde auch gleich noch als ''​const''​ vereinbart. ​
 +Gewöhnen Sie sich an, Feldparameter mit Eingabedaten als konstant zu kennzeichnen! ​
 +Damit wird die Gefahrenquelle gestopft, ​
 +in der ersten Version der Konto-Klasse bei ''​strcpy()'' ​
 +die Reihenfolge der Parameter zu verwechseln:​
 +
 +<code cpp>
 +Konto::​Konto(char inhabername[40],​ int anfangseinzahlung)
 +{
 +  strcpy( inhabername,​ name ); // Peng!
 +  saldo = anfangseinzahlung;​
 +}
 +</​code>​
 +
 +Ich weiß, das passiert echten Programmierern nicht. Wirklich. Niemals. ​
 +Nur Anfängern. Sie merken (vielleicht) beim Test zur Laufzeit, ​
 +dass hier etwas schiefgelaufen ist. Vielleicht stürzt das Testprogramm ab. 
 +Möglicherweise wundern Sie sich nur, 
 +dass das Konto im Schweizer-Banken-Stil namenlos bleibt. ​
 +Ungünstigstenfalls lauert der Fehler solange unentdeckt, ​
 +bis er entsprechend Murphys Gesetz mit maximalem Erfolg zuschlagen kann. 
 +(Murphys Gesetz wurde schließlich auch nicht von Murphy entdeckt, ​
 +sondern von einem anderen Mann gleichen Namens.)
 +
 +Das kleine Wörtchen ''​const''​ lässt den Compiler sofort beim Übersetzen Krach schlagen. ​
 +Es erspart stundenlange erfolglose, nervenaufreibende Fehlersuche und Debuggersitzungen.
 +
 +==== (Zeitweise) konstante Objekte ====
 +
 +Sie wollen Ihren Kontoauszug holen. ​
 +Sie könnten sich dazu eine Kopie des Kontos anlegen (sic!), den Auszug drucken, ​
 +das (farb)kopierte Geld abheben und anschließend verjubeln:
 +
 +<code cpp>
 +void auszug(Konto konto)
 +{
 +  std::cout << "Name des Inhabers: " << konto.inhaber()
 +            << "​Guthaben in Taler: " << konto.guthaben() << '​\n';​
 +  /*
 +  int falschgeld = konto.abheben( konto.guthaben() );
 +  std::cout << "Wir geben gleich aus soviel " << falschgeld << '​\n';​
 +  */
 +}
 +</​code>​
 +
 +Das originale Konto ist immer noch voll. 
 +Den auskommentierten Teil habe ich nur eingefügt, um zu verdeutlichen: ​
 +Als Erbe von C zeigen Funktionsparameter in C++ Wertverhalten, ​
 +verwirklicht durch Kopien. ​
 +Referenzen müssen in C++ extra gekennzeichnet werden:
 +
 +<code cpp>
 +void auszug(Konto&​ konto)
 +{
 +  std::cout << "Name des Inhabers: " << konto.inhaber()
 +            << "​Guthaben in Taler: " << konto.guthaben() << '​\n';​
 +  /*
 +  int echtesgeld = konto.abheben( konto.guthaben() );
 +  std::cout << "Wir geben gleich aus soviel " << echtesgeld << '​\n';​
 +  */
 +}
 +</​code>​
 +
 +Der auskommentierte Teil würde die Kunden sehr ärgerlich machen, ​
 +weil deren Geld dann wirklich verjubelt würde. ​
 +Durch Abfragen sollte Ihr Kontostand nicht niedriger werden. ​
 +Obwohl: Manche Kreditinstitute lassen sich das Ausdrucken noch extra bezahlen, ​
 +trotzdem sie schon einen Teil der Kapitalerträge aus der Verfügung ​
 +über Ihr Guthaben einstreichen. ​
 +Verbitten Sie sich solchen Unfug:
 +
 +<code cpp>
 +void auszug(const Konto& konto)
 +{
 +   ​std::​cout << "Name des Inhabers: " << konto.inhaber()
 +             <<​ "​Guthaben in Taler: " << konto.guthaben() << '​\n';​
 +}
 +</​code>​
 +
 +Nanu? Wieso geht das nicht? Der Compiler meint, ​
 +dass er von einem konstanten Konto weder den Inhaber noch das Guthaben abfragen kann. 
 +Daran ändert sich auch nichts, ​
 +wenn die Funktionsnamen in ''​getInhaber()''​ und ''​getGuthaben()''​ umbenannt werden.
 +
 +Beim Klassenentwurf legen Sie fest, was welche Methode tun soll: 
 +''​einzahlen(betrag)''​ und ''​abheben(betrag)''​ werden den Saldo vermutlich ändern wollen. ​
 +Methoden mit ''​get...()'',​ ''​hat..()'',​ ''​has...()'',​ ''​ist...()''​ oder 
 +''​is...()''​ im Namen (deutsch, englisch, oder grauenhaft dänglisch) ​
 +fragen nur Werte ab oder berechnen diese. ​
 +Für ändernde (schreibende) Methoden wählen manche Designer gern Namen wie 
 +''​set...()'',​ ''​setze...()''​ usw.
 +
 +Den C++-Compiler rühren solche Moden nicht. ​
 +Er muss den Quelltext nicht verstehen, nur übersetzen und dabei auf Korrektheit prüfen. ​
 +Dazu gehört, dass auch die Konstantheit gewahrt bleibt. ​
 +Notfalls dadurch, dass der Quelltext nicht übersetzt wird. 
 +Spätestens hier rudern Anfänger freiwillig zurück in den Abgrund ​
 +und lassen alle konstanten Vorsätze fahren. ​
 +Hauptsache, es übersetzt und wird lauffähig. Egal wie. Vergiss ''​const''​. ​
 +Alles für die Katz. Wirklich? ​
 +Lassen Sie sich nicht so ins Bockshorn jagen. Nicht aufgeben, weiterlesen.
 +
 +==== Konstante Methoden ====
 +
 +Die Unterscheidung in lesende Methoden (accessors) und 
 +schreibende Methoden (mutators) ist richtig und wichtig. ​
 +Wie die Funktionen benannt werden, ist eine Frage des Stils, nicht der Korrektheit. ​
 +Der Entwickler der ''<​iostream>''​-Bibliothek,​ Jerry Schwarz, ​
 +hat sich nicht an ''​set...()''​ und ''​get...()''​ gehalten. Es ist klar, 
 +dass die Methode ohne Parameter nur einen Wert zurückgeben (holen) kann, 
 +während die gleichnamige Methode mit Parameter einen Wert übernimmt, ​
 +der die Einstellung ändern kann:
 +
 +<code cpp>
 +  int stellen = std::​cout.precision();  ​
 +  std::​cout.precision(10); ​             // Wir wollen'​s genauer als 6 Ziffern!
 +</​code>​
 +
 +Vorsilben sind in solchen Sprachen notwendig, ​
 +die das Überladen von Funktionsnamen nicht beherrschen. ​
 +Aus dieser überlieferten Erfahrung stammt wohl die Praxis mit den Vorsilben. ​
 +In den modernen OOP-Sprachen ist sie genauso diskussionswürdig wie die 
 +(sogenannte [[ungarn|ungarische]]) Simonyi-Notation. ​
 +Befürworter sagen, die Vorsilben erhöhten die Lesbarkeit, ​
 +indem deutlich (unmissverständlich?​) geschrieben steht, ​
 +welche Rolle die Methode spielt.
 +
 +Kritiker finden, die Lesbarkeit sei herabgesetzt, ​
 +da ''​istFormale szsetVorsilben''​ den ''​szgetTextfluss istEmpfindlich setStören''​. ​
 +''​istStimmt''​s? ​
 +Der dauernde Groß-/​klein-Wechsel erschwert Lesen und Schreiben gleichermaßen. ​
 +''​Sehr_lange_zusammengesetzte_Bezeichner''​ können besser mit Unterstrichen verkoppelt werden. ​
 +Zudem können Bezeichner auch lügen. Vielleicht muss eine Methode sich 
 +im Verlaufe ihrer Entwicklung Schreibzugriffen öffnen oder verschließen. ​
 +Wird dann ''​get...()''​ / ''​set...()''​ überall geändert? ​
 +Es ist zu befürchten,​ nein. 
 +Schließlich findet Software-Entwicklung fast immer unter Zeitdruck statt.
 +
 +Wer garantiert dann die Korrektheit?​ Dies ist eine Aufgabe, ​
 +die nicht fehlerbehafteten und allzu toleranzwilligen menschlichen Wesen überlassen bleiben kann. 
 +Zumal es zuverlässige Werkzeuge gibt, die ''​const''​-Korrektheit überwachen. Compiler.
 +
 +Woher soll aber der Compiler "​wissen",​ dass die Funktion ''​guthaben()'' ​
 +oder ''​getGuthaben()''​ nicht ihr Guthaben verändert? ​
 +Sie müssen es ihm verständlich machen. ​
 +Mit den Schlüsselwort ''​const''​ an der richtigen Stelle. ​
 +Studieren Sie z.B. auch die Definitionen der ''<​iostream>''​-Klassen. ​
 +Finden sie das ''​const'':​
 +
 +<code cpp>
 +class Konto
 +{
 +public:
 +  std::string inhaber() const { return name;  }
 +  int        guthaben() const { return saldo; }
 +  //...
 +};
 +</​code>​
 +
 +Das hinter der schließenden runden Klammer stehende Schlüsselwort gehört, ​
 +genau wie die Typen der Parameterliste mit zum Funktionsnamen. ​
 +Wird die ''​const''​-Methode außerhalb der Klassenschnittstelle implementiert, ​
 +muss das ''​const''​ wieder mit aufgeführt werden:
 +
 +<code cpp>
 +int Konto::​guthaben() const 
 +{
 +  return saldo; ​
 +}
 +</​code>​
 +
 +Wird es vergessen, entstehen ein Compilerfehler ​
 +(implementierte Funktion ''​guthaben()''​ ohne ''​const'' ​
 +in der Klassenschnittstelle Konto nicht aufgeführt) und ein Linkerfehler ​
 +(Methode ''​guthaben() const''​ nicht implementiert). ​
 +Es ist möglich, dass es zwei Methoden gibt, 
 +die sich nur im nachgestellten ''​const''​ unterscheiden. ​
 +Ein wichtiges Beispiel ist der Indexoperator ''​operator[]''​.
 +
 +==== Konstanter Zustand ====
 +
 +Die ''​const''​-Markierung von Methoden versichert den Nutzer nach außen, ​
 +dass die Methoden nichts am Inhalt des Objektes ändern. ​
 +Bei konstanten Objekten können nur solche ''​const''​-Methoden aufgerufen werden.
 +
 +Der andere Aspekt ist aber genauso wichtig: ​
 +Was nach außen versichert wird, muss nach innen garantiert und kontrolliert werden. ​
 +Durch ''​const''​ markierte Methoden dürfen keinen Attributwert des Objektes ändern, ​
 +zu dem sie gehören. Zudem dürfen aus ''​const''​-Methoden heraus nur 
 +ebensolche als ''​const''​ deklarierte Methoden aufgerufen werden, ​
 +die den Zustand des Objektes nicht ändern, damit die Konstantheit gewahrt bleibt. ​
 +Setzt man ''​const''​ ein, sind manche Verirrungen wie die folgende schnell entdeckt. ​
 +Sie stammt (etwas abgewandelt und zugespitzt) ​
 +aus Prüfungsarbeiten angehender Programmierer:​
 +
 +<code cpp>
 +double Ware::​getBruttopreis()
 +{
 +  return netto += netto*mehrwertsteuersatz/​100.0;​
 +}
 +</​code>​
 +
 +Sie werden Ihren Augen nicht trauen. Die Ware wird jedesmal, ​
 +wenn Sie aufs Preisschild schauen, um 16 Prozent teurer! ​
 +Bei dieser Inflation können wir den Maastricht-Kriterien ade sagen. ​
 +Halten Sie die Preise konstant:
 +
 +<code cpp>
 +double Ware::​getBruttopreis() const
 +{
 +  return netto + netto*mehrwertsteuersatz/​100.0;​
 +}
 +</​code>​
 +
 +Das Erhöhen des Nettopreises mit ''​+=''​ wird in einer ''​const''​-Methode ​
 +vom Compiler beanstandet. Da kann etwas nicht stimmen. ​
 +Der Fehler wird erkannt, bevor er im ausführbaren Code wirksam werden kann. 
 +Lassen Sie den Compiler mit über die Qualität des Quelltextes wachen. ​
 +Der 21. Ratschlag von Scott Meyers [Meyers] lautet: ​
 +
 +> Use const whenever possible.
 +
 +Leider ist es mir nicht gelungen, mich kurz zu fassen. ​
 +Ich war schon in der Schule wegen langer Aufsätze gefürchtet. ​
 +Ich tröste mich (und hoffentlich Sie auch) mit dem Hinweis, ​
 +dass Bruce Eckel [Eckel] dem Schlüsselwort ''​const'' ​
 +ein ganzes Buchkapitel gewidmet hat. Sie sollten es lesen. Es ist es wert.
 +
 +===== Quellen =====
 +
 +  * [PI] Jörg Arndt, Christoph Haenel: Pi. Algorithmen,​ Computer, Arithmetik. Springer (1998) S. 109. 
 +  * [D&E] Bjarne Stroustrup: Design und Entwicklung von C++. Addison-Wesley (1994) S. 112. 
 +  * [Meyers] Scott Meyers: Effective C++. 50 ways to improve your programs. Addison-Wesley (1992). ​
 +  * [Eckel] Bruce Eckel: Thinking in C++. 2nd edn. Prentice-Hall (2000). ​
 +  * [Hoff] Todd Hoff: C++ Coding standards. http://​www.possibility.com/​Cpp/ ​
 +  * [FAQ] Marshall Cline: C++ FAQ Lite. http://​www.parashift.com/​c++-faq-lite/​const-correctness.html ​
 +  * [Loder] Chad Loder: Const Correctness. C-Scene Issue #2. http://​www.elitecoders.de/​mags/​cscene/​CS2/​CS2-01.html ​
 +
 +Weiterführende Literatur:
 +
 +  * [Stroustrup] Bjarne Stroustrup: Die C++ Programmiersprache. 3. Aufl. Addison-Wesley (1998) S.102-105.
 +
  
lernen/const.txt · Zuletzt geändert: 2014-07-13 16:14 (Externe Bearbeitung)