Konstanten

Martin Kompf

Die Deklaration konstanter Objekte, Parameter und Memberfunktionen ist eine wichtige Komponente der strukturierten Programmierung und hilft bei der Vermeidung von Fehlern.

Im Prinzip ist es möglich, komplette Softwareprojekte in C++ zu realisieren, ohne ein einziges Mal das Schlüsselwort const zu verwenden. Mag der einzelne Programmierer damit vielleicht noch klarkommen, so zeigt sich beim Austausch von Code im Entwicklerteam oder der Benutzung von Libraries, wie durch geschickte const Deklarationen die Qualität der Software steigt.

Betrachten wir folgenden Programmteil:

extern void check_format( string& in);

void sample1() {
    string a = "12345";
    check_format( a);
    cout << a << endl;
}

Hier wird in der Funktion sample1() die als extern deklarierte Funktion check_format() mit dem Parameter a aufgerufen. Die Übergabe erfolgt dabei per Referenz, um sich das Kopieren des kompletten Strings zu ersparen, wie es bei Übergabe per Value auftreten würde. Wenn anschließend a noch anderweitig verwendet werden soll, so stellt sich dem Entwickler die Frage, ob denn check_format() den Übergabeparameter verändert hat. Das wäre ja möglich, da die Übergabe per Referenz erfolgte. Wenn keine weitere Dokumentation vorliegt, so kann man hier nur raten und hoffen oder sicherheitshalber eine Kopie von a anlegen - alles unbefriedigende Dinge. Was aber, wenn die Deklaration von check_format() folgendermaßen aussehen würde:

extern void check_format( const string& in);

Dann ist klar, dass die Funktion check_format() den Wert des Parameters in nicht verändern darf. Der Anwender kann nun davon ausgehen, dass a nach dem Aufruf von check_format() den gleichen Wert wie vorher hat, obwohl er per Referenz übergeben wurde. Ein Versuch, in der Implementierung von check_format den Wert von in zu ändern, führt zu einer Fehlermeldung beim Kompilieren:

void check_format( const string& in)
{
    // ...
    in.erase(); // Fehlermeldung beim Compile
}

Dieses Prinzip lässt sich auch auf ganze Objekte anwenden. Zum Beispiel könnte die Definition einer einfachen Klasse, die einen Stack implementieren soll, folgendermaßen aussehen:

class Stack {
public:
    void push( int elem);
    int pop();
    int size() const;
};

Während push() und pop() den Inhalt des Stacks verändern, inspiziert size() nur seine Größe und verändert ihn dabei nicht. Deswegen wird size() als const deklariert. Der Sinn dieser Maßnahme offenbart sich in diesem Code:

void sample2( const Stack& s)
{
    cout << s.size(); // OK
    s.pop(); // Fehlermeldung beim Compile
}

Da das an die Funktion übergebene Stack-Objekt s als const deklariert ist, führt der Versuch, es innerhalb dieser Funktion mittels s.pop() zu modifizieren, zum Fehler während des Kompilierens. Der Aufruf von s.size() dagegen ist in Ordnung, da die Memberfunktion size() ebenfalls als const deklariert wurde.

Das Schlüsselwort const kann weiterhin dazu verwendet werden, um konstante Werte zu deklarieren. Die Anweisungen

const double eu2dm = 1.95583;
const double dm2eu = 1.0/eu2dm;

deklarieren die beiden Konstanten eu2dm und dm2eu. Somit kann man im weiteren Programmverlauf mit den symbolischen Namen eu2dm und dm2eu arbeiten, anstatt überall 1.95583 hinzuschreiben. Der Vorteil gegenüber einer normalen Variablendeklaration wiederum ist, dass bereits der Compiler die Konstanten auswerten kann.

Schließlich ist es noch möglich, den Rückgabewert einer Funktion als const festzulegen. Eine Kodierung der Form

const char* error_message( int i)
{
    //...
    return "range_error";
}

bewirkt, dass error_message() zwar in einem Kontext wie

    cerr << error_message( 0) << endl; // OK

aufgerufen werden kann, der Versuch der Zuweisung

    char* e = error_message( 5);  // Fehler

jedoch zu einem Fehler führt.

Wichtig ist, dass die Verwendung von const keinen zusätzlichen Overhead bei der Ausführung des Programms generiert. Alle Prüfungen finden während des Kompilierens statt. Der etwas höhere Design- und Implementierungsaufwand lohnt sich; er resultiert in verbesserter Lesbarkeit des Codes, zusätzlicher statischer Typsicherheit und erhöhter Qualität der Software.