Deklarationen in C++
Alle bisher vorgestellten Definitionen sind auch Deklarationen. Eine Deklaration legt den Typ eines Bezeichners fest. Eine Definition legt darüber hinaus auch fest, an welche Entität ein Bezeichner gebunden ist. Es gibt aber auch Deklarationen, die keine Definitionen sind. Diese geben nur den Typ eines Bezeichners an, ohne dem Bezeichner eine Entität zuzuordnen. Bei einer Funktionsdeklaration sagt man auch, solch eine Deklaration lege den Prototyp einer Funktion fest, um zu sagen, daß der Leser nun ihren Typ kenne. Zusammen mit der Angabe der nötigen Kopfdirektive bildet eine Deklaration auch die Synopsis.
Mindestens der Typ eines Bezeichners muß bei Verwendung des Bezeichners bekannt sein, damit der Leser des Programms (z.B. der Übersetzer) prüfen kann, ob die Verwendung hinsichtlich des Typs korrekt ist, ohne zuerst die gesamte (restliche) Datei lesen zu müssen. Soll ein Bezeichner erst nach der Verwendung im Quelltext definiert werden, dann muß er also vor der Verwendung zumindest deklariert werden.
In dem folgenden Beispielprogramm wird die Funktion "hallo" zunächst ohne Definition deklariert. Dabei wird nur der Typ festgelegt. Die Funktion kann dann schon verwendet werden, weil der Übersetzer ihren Typ an der Stelle des Aufrufs kennt. Nach der ersten Verwendung folgt weiter unten im Quelltext die Definition (Implementation). Der Übersetzer liest den Quelltext nur einmal von vorne bis hinten durch. Erst der Verbinder wird dann die Adresse der Funktion "hallo" an den Stellen des Aufrufs dieser Funktion einsetzen, das ermöglicht dann den Aufruf. Der Verbinder kann solch Ersetzungen auch dann erzeugen, wenn eine Definition im Programmtext nach der Verwendung erfolgt.
Es ist erlaubt, einen Bezeichner mehrfach zu deklarieren, wenn alle Deklarationen übereinstimmen. Eine Definition eines Bezeichners darf jedoch in der Regel nur einmal innerhalb eines Namensraumes und Gültigkeitsbereichs erfolgen (abgesehen von bestimmten Ausnahmen, wie Überladungen).
deklaration.cpp#include <iostream>
#include <ostream>
void hallo(); // Deklaration von "hallo" (ohne Definition)
int main(){
hallo // Verwendung von "hallo"
(); }
void hallo(); // identische Redeklaration ist erlaubt
void hallo() // Definition und erneute Deklaration von "hallo"
{ ::std::cout << "Hallo!\n"; } //::std::coutHallo!
Ohne die Deklaration kennte der C++ -Leser die Funktion beim Aufruf nicht gut genug, um die Richtigkeit ihrer Verwendung zu prüfen.
deklaration.txt#include <iostream>
#include <ostream>
int main(){ hallo(); }
void hallo(){ ::std::cout << "Hallo!\n"; }Konsole"deklaration.txt", line 3: error: identifier "hallo" is undefined
int main(){ hallo(); }
^
1 error detected in the compilation of "deklaration.txt".
Wenn eine Funktion vor ihrer ersten Verwendung definiert wird, ist eine zusätzliche Deklaration nicht nötig.
definition.cpp#include <iostream>
#include <ostream>
void hallo() // Definition von "hallo"
{ ::std::cout << "Hallo!\n"; } //
int main(){
hallo // Verwendung von "hallo"
(); }::std::coutHallo!
Natürlich könnte der Eindruck entstehen, daß Deklarationen, die nicht gleichzeitig Definitionen sind, nicht so dringend benötigt werden, weil Funktionen ja vor ihrer Verwendung definiert werden können. Das ist aber nicht immer möglich. Deklarationen, die nicht gleichzeitig Definitionen sind, spielen eine wichtige Rolle. Beispielsweise kann ein C++ -Programm Funktionen der Standardbibliothek benutzen, die es nicht selber definiert hat. Das geht aber nur dann, wenn diese vorher deklariert wurden. Diese Deklaration wird von den Kopfdirektiven, wie beispielsweise "#include <iostream>" bewirkt, ohne daß der Quelltext der Definition von Standardfunktionen zugänglich sein muß.
Lokale Deklarationen
Innerhalb eines Blocks ist die Definition einer Funktion nicht möglich. Eine Funktion kann jedoch innerhalb eines Blocks deklariert werden. Der Typ der so deklarierten Funktion ist dann nur innerhalb dieses Blocks bekannt. Daher spricht man auch von einer (zu diesem Block) lokalen Deklaration.
deklaration1.cpp#include <iostream>
#include <ostream>
int main(){
void hallo(); // lokale Deklaration von "hallo"
hallo // Verwendung von "hallo"
(); }
void hallo() // Definition von "hallo"
{ ::std::cout << "Hallo!\n"; } //::std::coutHallo!
Leere Klammern oder Schlüsselwort "void"?
Sowohl in einer Definition als auch in einer Deklaration können die runden Klammern leer sein oder das Schlüsselwort "void" enthalten, das ändert nichts an der Bedeutung. In dem folgenden Programmbeispiel "voidpar.cpp" gilt die zweite reine Deklaration daher als identische Redeklaration. Auch in der Definition kann das Schlüsselwort "void" in die runden Klammern geschrieben werden.
voidpar.cpp#include <iostream>
#include <ostream>
void hallo(); // Deklaration von "hallo" (ohne Definition)
void hallo( void ); // identische Redeklaration ist erlaubt
void hallo( void ) // Definition und erneute Deklaration von "hallo"
{ ::std::cout << "Hallo!\n"; } //
int main(){ hallo(); }::std::coutHallo!
Es wird empfohlen, das Schlüsselwort "void" nie in den runden Klammern zu verwenden, sondern diese gegebenenfalls immer leer zu lassen, da dies bei gleicher Bedeutung kürzer ist.
Anhang *
»void« als Parameterliste
“The parameter list (void) is equivalent to the empty parameter list.”, »ISO/IEC 14882:1998(E)« und »ISO/IEC 14882:2003(E)«, 8.3.5 p2.