C++ Kurs

Statische Member

Die Themen:

Statische Eigenschaften
Statische Memberfunktionen
Beispiel und Übung

Statische Eigenschaften

Die Besonderheit von statischen Eigenschaften liegt darin, dass diese Eigenschaften zunächst für alle Objekte einer Klasse nur ein einziges Mal vorhanden sind. Desweiteren werden statische Eigenschaften standardmäßig immer mit 0 initialisiert und auf sie kann auch ohne Bezug auf ein bestimmtes Objekt zugegriffen werden (wenn sie public sind).

Um eine statische Eigenschaft zu definieren sind zwei Schritte erforderlich:

  1. Zuerst muss innerhalb der Klasse die statische Eigenschaft deklariert werden. Bei der Deklaration ist vor dem Datentyp der Eigenschaft das Schlüsselwort static anzugeben.
  2. Da die statische Eigenschaft für alle Objekte der Klasse nur einmal vorhanden ist, muss sie explizit außerhalb der Klasse definiert werden. Die Definition erfolgt prinzipiell genauso wie die Definition einer 'normalen' Variable, jedoch wird vor dem Eigenschaftsnamen noch die zugehörige Klasse angegeben. Beachten Sie bitte, dass hier nicht mehr das Schlüsselwort static stehen darf!

// Klassendefinition
class Window
{
   ...
   static short noOfWin;    // Deklaration der statischen Eigenschaft
   ...
};
// Definition der statischen Eigenschaft
short Window::noOfWin = 0;

Selbstverständlich können Sie eine statische Eigenschaft bei ihrer Definition auch mit einem Wert initialisieren, so wie oben angegeben.

Der Zugriff auf eine statische Eigenschaft aus einer Memberfunktion seiner Klasse heraus erfolgt gleich wie der Zugriff auf 'normale' Eigenschaften. Ist die statische Eigenschaft public, so kann auch von außerhalb der Klasse ohne Bezug auf ein Objekt auf diese Eigenschaft zugegriffen werden. In diesem Fall müssen Sie (wie unten angegeben) den Klassennamen vor den Eigenschaftsnamen stellen. Denken Sie immer daran: eine statische Eigenschaft besitzt keine Objektzugehörigkeit!


// Klassendefinition
class Window
{
   ...
 public:
   Window();               // ctor
   ~Window();              // dtor
   static short noOfWin;   // Statische Eigenschaft
   ...
};
// Definition der statischen Eigenschaft
short Window::noOfWin = 0;
// Zugriff aus Memberfunktion heraus
Window::Window()
{
   ...
   noOfWin++;     // Anzahl Fenster inkrementieren
}
Window::~Window()
{
   ...
   noOfWin--;     // Anzahl Fenster dekrementieren
}

// main() Funktion
main()
{
   ...
   if (Window::noOfWin)    // Falls bereits Fenster erstellt wurden
      ....
}

Im Beispiel sehen Sie einen Anwendungsfall für eine statische Eigenschaft. Die Eigenschaft noOfWin wird hier als Zähler für die Anzahl der erstellten Fensterobjekte verwendet. Da die statische Eigenschaften mit 0 initialisiert wird, braucht im Konstruktor dieser Zähler einfach nur um eins erhöht und im Destruktor wieder um eins erniedrigt werden. Selbstverständlich sollte in der Praxis dieser Zähler nicht, wie oben angegeben, public sein. Dies dient nur zur Demonstration des direkten Zugriffs auf eine statische Eigenschaft.

Statische Memberfunktionen

Sehen wir uns nun die statischen Memberfunktionen einer Klasse an. Statische Memberfunktionen besitzen eine Reihe von Einschränkungen:

  1. Sie haben nur Zugriff auf die statische Member (Eigenschaften und weitere Memberfunktionen) einer Klasse.
  2. Statische Memberfunktion erhalten keinen this-Zeiger (wird in der nächsten Lektion gleich erklärt.
  3. Es darf keine statische und nicht-statische Memberfunktion mit der gleichen Signatur (Name plus Parameter) geben.
  4. Statische Memberfunktionen können nicht virtuell sein (auch das wird später noch erklärt).
  5. Statische Memberfunktionen können nicht const oder volatile sein.

Wozu statische Memberfunktionen trotz dieser vielen Einschränkungen dennoch nützlich sein können, das erfahren Sie gleich im Beispiel und der Übung. Doch sehen wir uns zunächst an, wie eine statische Memberfunktion definiert wird.

Um eine Memberfunktion als statische Memberfunktion zu definieren, wird bei ihrer Deklaration bzw. Definition innerhalb der Klasse das Schlüsselwort static vorangestellt. Wird die Memberfunktion außerhalb der Klasse definiert, so darf hier das Schlüsselwort static nicht mehr angegeben werden (siehe Beispiel).


// Klassendefinition
class Window
{
   ...
   static short noOfWin;        // Statische Eigenschaft
 public:
   static short GetNoOfWin();   // Deklaration statische Memberfunktion
   ...
};
// Definition der statischen Eigenschaft
short Window::noOfWin = 0;
// Definition der statischen Memberfunktion
void Window::GetNoOfWin()
{
   return noOfWin;
}

Beachten Sie aber, dass statische Memberfunktionen nur Zugriff auf statische Member haben!

Der Aufruf einer statischen Memberfunktion kann auf drei verschiedene Arten erfolgen:

  1. entweder durch sich selbst (hier nicht aufgeführt, rekursive Memberfunktion!)
  2. ohne Bezug auf ein Objekt, indem beim Aufruf vor dem Namen der Memberfunktion der Klassenname gestellt wird.
  3. in Verbindung mit einem beliebigen Objekt.

Die letzten beiden Fälle sind im nachfolgenden Beispiel dargestellt.


// Klassendefinition
class Window
{
   ...     // Definitionen wie oben
};
// Definition der statischen Eigenschaft
short Window::noOfWin = 0;
// Definition der statischen Memberfunktion
void Window::GetNoOfWin()
{
   return noOfWin;
}

// main() Funktion
int main()
{
   Window myWin;
   short winCount;
   ...
   winCount = Window::GetNoOfWin();
   winCount = myWin.GetNoOfWin();
   ...
}

Beispiel und Übung

Beispiel:

Das Beispiel zeigt die Anwendung von statischen Eigenschaften anhand einer Fensterklasse auf.

Alle Fenster der Fensterklasse sollen immer eine gemeinsame Titel- und Fensterfarbe besitzen. Die Farbwerte werden dazu als statische unsigned long Daten innerhalb der Fensterklasse definiert. Zum Setzen der Farben werden zwei statische Memberfunktionen SetTitleColor(...) und SetBackColor(...) verwendet. Außer den Farbwerten enthält die Fensterklasse noch eine nicht-statische Eigenschaft für den Fenstertitel.

In main() werden zuerst die beiden Farben gesetzt. Das Setzen der Farbwerte erfolgt hierbei direkt ohne Bezug auf ein Fensterobjekt. Anschließend werden zwei Fensterobjekte definiert und dann deren Fenstereigenschaften ausgegeben. Danach wird die Fensterfarbe über ein Fensterobjekt umgesetzt. Da die Fensterfarbe eine statische Eigenschaft ist, wirkt sich dieses aber auf alle Fensterobjekte aus.

Titelfarbe: 0xff, Titel: Mein Fenster
Fensterfarbe: 0x808080
Titelfarbe: 0xff, Titel: Dein Fenster
Fensterfarbe: 0x808080

Titelfarbe: 0xff, Titel: Mein Fenster
Fensterfarbe: 0x80ff
Titelfarbe: 0xff, Titel: Dein Fenster
Fensterfarbe: 0x80ff


// Beispiel zu statischen Member

// Zuerst Dateien einbinden

#include <iostream>
#include <string>

using std::cout;
using std::endl;
using std::string;

// Fensterklasse
// Alle Fenster der Klassen haben eine gemeinsame
// Titel- und Fensterfarbe aber unterschiedliche Titel

class Window
{
   static unsigned long titleColor;   // Titelfarbe
   static unsigned long winColor;     // Fensterfarbe
   string title;                      // Fenstertitel
 public:
   // ctor
   // Beachte: string ist ein Objekt und sollte deshalb
   // per Initialisiererliste initialisiert werden

   Window(const char* t): title(t)
   {}
   // Fenster darstellen
   void Draw() const;
   // statische Memberfunktionen zum Setzen der Farben
   static void SetTitleColor(unsigned char r, unsigned char g, unsigned char b);
   static void SetBackColor(unsigned char r, unsigned char g, unsigned char b);
};

// Statische Eigenschaften der Window-Klasse definieren
unsigned long Window::titleColor;
unsigned long Window::winColor;

// Fenstereigenschaften ausgeben
void Window::Draw() const
{
   cout << std::hex << std::showbase;
   cout << "Titelfarbe: " << titleColor;
   cout << ", Titel: " << title << endl;
   cout << "Fensterfarbe: " << winColor << endl;
   cout << std::dec << std::noshowbase;
}
// Titelfarbe setzen
void Window::SetTitleColor(unsigned char r, unsigned char g, unsigned char b)
{
   titleColor = (r<<16) + (g<<8) + b;
}
// Fensterfarbe setzen
void Window::SetBackColor(unsigned char r, unsigned char g, unsigned char b)
{
   winColor = (r<<16) + (g<<8) + b;
}

// main() Funktion
int main()
{
   // Fenster- und Titelfarbe setzen
   Window::SetBackColor(0x80,0x80,0x80);
   Window::SetTitleColor(0x00,0x00,0xff);

   // Zwei Fenster definieren
   Window myWin("Mein Fenster");
   Window yourWin("Dein Fenster");
   // und deren Eigenschaften ausgeben
   myWin.Draw();
   yourWin.Draw();

   // Fensterfarbe umsetzen
   // Beachte: Es wird die Fensterfarbe für alle Fenster
   // umgesetzt da die Fensterfarbe eine statische Eigenschaft ist

   myWin.SetBackColor(0x00,0x80,0xff);

   // Fenstereigenschaften erneut ausgeben
   cout << endl;
   myWin.Draw();
   yourWin.Draw();
}

Übung:

Entwickeln Sie eine Klasse zur fiktiven Ansteuerung einer Ausgabeschnittstelle. Es sollen beliebig viele Objekte von dieser Klasse erstellt werden können, die über diese Schnittstelle Ausgaben tätigen können.

Da wir uns hier aber nicht mit der Ansteuerung einer realen Schnittstelle befassen wollen, sollen die Ausgaben auf die Schnittstelle als Bildschirmausgaben erfolgen. Für die Ausgabe auf die Schnittstelle ist eine entsprechende Memberfunktion vorzusehen, die als Parameter den auszugebenden Text erhalten soll.

Hat ein Objekt die Schnittstelle für eine Ausgabe erst einmal belegt, so belegt es die Schnittstelle so lange, bis es diese über eine ebenfalls zu schreibende Memberfunktion explizit wieder freigibt. Versuchen andere Objekte in der Zwischenzeit auf die belegte Schnittstelle etwas auszugeben, so erfolgt die Ausgabe einer Fehlermeldung.

Erzeugen Sie zwei Schnittstellen-Objekte. Geben Sie dann über das erste Objekt zweimal hintereinander einen beliebigen Text aus. Beide Ausgaben sollten ohne Fehlermeldung erfolgen.

Versuchen Sie dann eine Ausgabe über das zweite Objekt. Diese Ausgabe sollte blockiert sein, da das erste Objekt noch die Schnittstelle belegt hat.

Anschließend gibt das erste Objekt die Schnittstelle frei und das zweite Objekt versucht nun erneut zwei Ausgaben über die Schnittstelle vorzunehmen. Beide Ausgaben sollten jetzt ebenfalls erfolgreich durchgeführt werden, da das erste Objekt die Schnittstelle wieder freigegeben hat.

Bei richtiger Lösung werden Sie eine Ausgabe in etwa der nachfolgend dargestellten Form erhalten.

1. Schreiben von Objekt A
2. Schreiben von Objekt A
FEHLER: COM-Port belegt!
Objekt A gibt COM wieder frei
2. Schreiben von Objekt B
3. Schreiben von Objekt B

Lösung ansehen!