====== Zugriff auf einen Namensraum ====== Wenn wir unsere Klassen nun in einen eigenen Namensraum legen, so müssen wir auch diesen Namensraum ansprechen können, um die Klassen verwenden zu können. Eine Klasse Token im Namensraum ChessGame liegt nunmal nicht im globalen Namensraum - im globalen Namensraum wird die Klasse Token also nicht gefunden. Der Zugriff geht auch hier mit dem Operator '::': namespace ChessGame { class Token { private: int Value; public: Token( int value ) : Value( value ) {} static int staticFunc( int a, int b ) { return a+b; } }; } int main( void ) { ChessGame::Token * myToken = new ChessGame::Token( 4711 ); int addition = ChessGame::Token::staticFunc( 1, 2 ); delete myToken; return 0; } An diesem Beispiel ist zu sehen, dass es keinen Unterscheid macht, ob man auf einen Namensraum zugreift oder auf statische Elemente einer Klasse. ===== Zugriff aus einem Namensraum heraus ===== Nun gibt es aber nicht nur den Fall, dass man in einen Namensraum hineingreifen möchte, sondern auch den Fall, dass man eine Klasse, Variable oder Funktion aus dem globalen Namensraum ansprechen möchte. Der globale Namensraum selbst hat keinen Namen, entsprechend schreibt man auch nichts vor den '::'-Operator: int globalVariable; namespace ChessGame { int GetGlobalVariable( void ) { // Zugriff auf eine Variable // im globalen Namensraum return ::globalVariable; } } ===== Zugriff in einen anderen Namensraum ===== Die letzte Möglichkeit, die auftreten kann, ist, dass man in einen anderen Namensraum zugreifen möchte. Dabei ist die Betrachtung zunächst vom aktuellen Namespace aus und schaut dann schrittweise nach oben. Findet sich also im aktuellen Namespace die gewünschte Klasse, so wird diese genommen, ansonsten guckt man im Namespace darüber nach und arbeitet sich so bis zum globalen Namespace hoch. #include namespace Internal { class Token { protected: int Value; public: Token( int value ) : Value( value ) { printf( "This is Internal::Token\n" ); } }; } namespace Compiler { namespace Internal { class Token { public: Token( int value ) { printf( "This is Compiler::Internal::Token\n" ); } }; } class Token : Internal::Token { public: Token( int token ) : Internal::Token( token ) {} }; } int main( void ) { Compiler::Token * token = new Compiler::Token( 4711 ); delete token; return 0; } Das Programm hier bietet eine gefährliche Situation, es gibt eine Klasse ''Internal::Token'' und es gibt eine Klasse ''Compiler::Internal::Token'' und die Klasse ''Compiler::Token'' muss nun entscheiden, von wem sie abgeleitet wird. Das ist so keineswegs wünschenswert, aber auch das ist in C++ eindeutig geregelt - man muss halt nur wissen, wie. Wird das Programm kompiliert, so soll die Klasse ''Compiler::Token'' von der Klasse ''Internal::Token'' abgeleitet werden. Im Namensraum ''Compiler'' gibt es einen Namensraum Internal und darin die gesuchte Klasse Token. Also wird diese genommen. Wird das Programm ausgeführt, erhält man: This is Compiler::Internal::Token Möchten wir nun, dass die andere Klasse ''Internal::Token'' verwendet wird, bei der sich der Namensraum ''Internal'' im globalen Namensraum befindet, müssen wir klarstellen, dass sich der Namensraum auf den globalen Namensraum beziehen soll: ''::Internal::Token''. Das entspricht der absoluten Pfadangabe in einem Dateisystem (z.B. ''/home/xin'' unter Unix oder ''C:\Programme'' unter Windows). Entsprechend ändern wir die ''Compiler::Token''-Klasse und setzen den '::'-Operator vor Internal: class Token : ::Internal::Token { public: Token( int token ) : ::Internal::Token( token ) {} }; Nun erhalten wir aus Ausgabe die Meldung aus dem ::Internal::Token-Konstruktor: This is Internal::Token Eine Verschachtelung wie in diesem Beispiel ist eigentlich nicht Ziel des ganzen, es geht in diesen Beispielen darum, die Funktionsweise zu demonstrieren. Normalerweise sollten die Namensräume eindeutig benannt werden. Aber dieses Beispiel zeigt, wie man aus einem Namensraum heraus in einen beliebigen anderen Namensraum verzweigen kann. ===== Der Namensraum std ===== ''std'' ist ein besonderer Namensraum, da hier alles abgelegt wird, was zu Standard-C++ gehört. So wird heutzutage printf auch nicht mehr mit eingebunden, sondern mit . Der Unterschied ist, dass ''printf'' und die ganzen anderen Standard-Funktionen dann im Namensraum ''std'' angesiedelt sind und somit nicht mehr den globalen Namensraum vollmüllen: #include int main( void ) { std::printf( "Hello C++\n" ); } Dies gilt für alle C-Header: [[c:lib:assert:|cassert]], [[c:lib:ctype:|cctype]], [[c:lib:errno:|cerrno]], [[c:lib:float:|cfloat]], [[c:lib:limits:|climits]], [[c:lib:locale:|clocale]], [[c:lib:math:|cmath]], [[c:lib:setjmp:|csetjmp]], [[c:lib:signal:|csignal]], [[c:lib:stdarg:|cstdarg]], [[c:lib:stddef:|cstddef]], [[c:lib:stdio:|cstdio]], [[c:lib:stdlib:|cstdlib]], [[c:lib:string:|cstring]], [[c:lib:time:|ctime]] ) hier muss nun bei allen Funktionen erst der Namensraum ''std'' (eigentlich ''::std'') vorangestellt werden. In die Standardlib von C++ sind jedoch eine Vielzahl von zusätzlichen Headern gelangt, insbesondere die [[cpp:stl:|STL (Standard Template Library]]. Auch hier befindet sich alles unter ''::std''. So kann eine Textausgabe unter C++ auch mit den sogenannten [[cpp:lib:iostream:|IOStreams]] durchgeführt werden: #include int main( void ) { std::cout << "Hallo C++" << std::endl; } [[c:lib:stdio:printf]], wie auch [[cpp:lib:iostream|cout]] haben ihre Berechtigung. In printf lebt der prozedurale Ansatz weiter, mit cout wird klassenorientiert gedacht und die Daten werden einer Klasse zugeführt. Am Schluss landet in beiden Fällen der Text auf dem Bildschirm.