static-Attribute in C++ Klassen

An dieser Stelle soll die Aussage

“Statische Klassenattribute müssen außerhalb der Klassendefinition initialisiert werden.”

ein wenig näher untersucht werden. Betrachtet sei dazu die folgende Klassendefinition:

class KlasseMitStaticElement {
public:
static int anzahl;
KlasseMitStaticElement() {
cout << "Konstruktor aufgerufen" << endl;
KlasseMitStaticElement::anzahl++;
}
};

Geht man nun nach der oben genannten Regel vor, so würde die naive Initialisierung (eigentlich eine Zuweisung, siehe Zusatz unten) so aussehen: KlasseMitStaticElement::anzahl = 0;. Leider resultiert diese Zeile in einen Kompilerfehler, es wird über das undefined symbol “KlasseMitStaticElement::anzahl” gemeckert. Faszinierenderweise gelingt die Initialisierung aber mit  int KlasseMitStaticElement::anzahl = 0; warum?

Dazu müssen Deklaration, Definition und Initialisierung Begrifflich sauber voneinander getrennt werden. Ich verweise dazu auf ein C++-Forum, in dem von Benjamin Kaufmann folgendes geschrieben ist:

  • Deklaration: Ein Programmausdruck der einen Namen in einen Scope ein- bzw. wiedereinführt.
  • Definition: Eine Deklaration die die Details einer Entität bekannt macht oder, im Fall von Variablen, die dazu führt, dass Speicher für die Entität reserviert wird. Eine Deklaration einer Klasse (struct, class, enum, union) Funktion oder Methode wird zu einer Definition, wenn auf die Deklaration ein in geschweiften Klammern eingeschlossener Block folgt.
    Variablendeklarationen sind immer auch Definitionen es sei denn, der Deklaration ist ein extern vorangestellt.
  • Initialisierung: Eine Definition mit expliziter Anfangswertzuweisung.

Nach obigen Versuchen stellen wir also fest, dass auch die Voranstellung von static im Klassenkontext dazu führt, dass eine ursprüngliche Definition nur eine Deklaration darstellt. In dem Beispielprogramm matstatic.zip habe ich als statisches Attribut keinen einfachen Datentyp genutzt, sondern eine Klasse mit Standardkonstruktor verwendet. So kann man sehen, dass die Definition der Variable nach der Klassendefinition dazu führt, dass der Standardkonstruktor aufgerufen wird.

Bezogen auf das obige Beispiel stellt man also fest, dass die explizite Initialisierung (und damit implizite Definition) der Variable anzahl nicht notwendig ist. Eine Definition wie  int KlassenMitStaticElement::anzahl; wäre ausreichend, da die integer-Variable damit automatisch vom Compiler mit 0 initialisiert wird.

Ob die automatische Initialisierung einen allgemeinen C++-Standard oder nur eine Laune meines Compilers darstellt, ist mir zwar nicht bekannt — eventuell kann ohne Anfangszuweisung der Schwachsinn, der vorher an der Speicherstelle der Variable stand, von uns weiter genutzt werden — aber die anfangs erwähnte Aussage muss korrigiert werden.

“Statische Klassenattribute müssen außerhalb der Klassendefinition DEFINIERT werden.”

Zusatz:

Über Initialisierungen zu sprechen, stellt sich als nicht ganz einfach heraus, da das Verhalten des Compilers nicht nur von der Anweisung selbst, sondern auch von ihrer Position im Quelltext abhängt. Eine implizite Initialisierung mit 0 wird für die Anweisung int a; als globale Definition vorgenommen, während die gleiche Anweisung im Funktionskontext einen undefinierten Wert für a behält.

Man könnte nun auf der einen Seite eine implizite und eine explizite Initialisierung unterscheiden (nach der Definition aus dem Forum wäre nur die explizite Initialisierung überhaupt eine Initialisierung), oder man geht dem Problem aus dem Weg, indem man jede Variablendefinition auch gleichzeitig mit einer expliziten Initialisierung im Quelltext versieht. Da nur Variablen mit eindeutig zugewiesenen Werten semantischen Sinn ergeben, kann man die ganz oben genannte Aussage als Merkregel sehr gut für die Praxis verwenden. Doch sollten wir ein wenig verallgemeinern:

“MERKREGEL: Alle Variablen müssen initialisiert werden. Bei statischen Klassenattributen geschieht dies außerhalb der Klassendefinition.”