====== Statisches Casten - static_cast ====== Ein ''static_cast'' wird wie folgt aufgebaut static_cast< neuerTyp >( object ) Zurückgegeben wird ein Objekt im gewünschten ''neuerTyp''. ''static_cast'' eignet sich für die Umformung von Zahlentypen, Zeigern und Referenzen. Zwischen Klassen werden unäre [[cpp:class:construct|Konstruktoren]], also Konstruktoren, die als einzigen Parameter den zu konvertierenden Datentyp besitzen (auch [[cpp:class:explicit|explizite]], siehe auch [[construct|Casten durch Konstruieren]]) und [[cpp:operator:convert|Konvertierungsoperatoren]] der zu Klasse, in der das vorhandene Objekt vorliegt. Soll ein ein Cast ohne Prüfung zur Laufzeit geschehen, so werden die Daten brachial umgebogen. Dabei kann es zu Datenverlusten kommen: double pi = 3.1415; int almostPi = pi; ====== Primitive ====== In diesem Fall würde der Nachkommateil abgeschnitten, es würden also Daten verloren gehen. Daher sollte der Compiler hier meckern. Der GCC tut dies: warning: converting to `int' from `double' Wenn der Entwickler sich jedoch sicher damit ist, was er da tut, also den Datenverlust berücksichtigt hat, so kann er diese Absicht durch einen ''static_cast'' dem Compiler mitteilen und der Compiler akzeptiert den Wunsch des Entwicklers. Das folgende Programm kompiliert ohne eine Warnung: #include int main(int argc, char **argv) { double pi = 3.1415; int almostPi = static_cast< int >( pi ); return 0; } Gleiches gilt für beispielsweise eine Umwandlung von int (32 Bit breit) zu short (16 Bit). Auch hier können Daten verloren gehen und der Entwickler muss sich sicher sein, dass er dies berücksichtigt hat. ====== Objekte ====== Wie zuvor beschrieben, sucht static_cast nach [[cpp:operator:convert|Konvertierungsoperatoren]] oder [[construct|konstruiert ein neues Objekt]]. Versuchen wir folgendes: #include "stdio.h" // Die Existenz einer Klasse Point3d muss zur Deklaration // von Point2d( Point3d & ) schonmal bekannt sein. class Point3d; class Point2d { public: int x, y; Point2d( int ax, int ay ) : x( ax ), y( ay ) {} explicit Point2d( Point3d & p3 ); }; class Point3d { public: int x, y, z; Point3d( int ax, int ay, int az ) : x( ax ), y( ay ), z( az ) {} }; // Da wir alles in einen Code packen müssen Point2d erst bekannt sein, // bevor wir darauf zugreifen können Point2d::Point2d( Point3d & p3 ) : x( p3.x ) , y( p3.y ) { printf( "Konstruiere Point2d\n" ); } int main(int argc, char **argv) { Point3d p3( 1, 2, 3 ); Point2d p2( 4, 5 ); p2 = p3; return 0; } Der GCC meldet: error: no match for 'operator=' in 'p2 = p3' da es keinen Zuweisungsoperator gibt. C++ darf auch nicht automatisch casten, da der unäre Konstruktor als [[cpp:class:explicit|explizit]] gekennzeichnet ist. Wir können also jetzt den [[construct|Konstruktor explizit]] aufrufen: p2 = Point2d( p3 ); oder statisch casten: p2 = static_cast< Point2d >( p3 ); static_cast wird sich damit für den Aufruf des Konstruktors entscheiden. Hier hilft static_cast also deutlich hervorzugeben, dass hier Daten verloren gehen (nämlich die z-Koordinate des Point3d). Falls es eine Auswahl zwischen einem [[cpp:operator:convert|Konvertierungsoperatoren]] und einer [[construct|Umformung durch Konstruktion]] gibt, so entscheidet sich static_cast für die [[construct|Umformung durch Konstruktion]], auch wenn der Konstruktor als [[cpp:class:explicit]] gekennzeichnet ist. ====== Zeiger und Referenzen ====== Basierend auf dem Programm im Abschnitt //Objekte// ändere ich hier die ''main''-Funktion wie folgt: int main(int argc, char **argv) { Point3d p3( 1, 2, 3 ); Point2d p2( 4, 5 ); Point2d * pp2; Point3d * pp3 = &p3; pp2 = static_cast< Point2d * >( pp3 ); // Fehlermeldung pp2 = (Point2d *) pp3; // wird akzeptiert return 0; } Wir haben nun zwei zusätzliche Zeiger, wobei ''pp2'' auf einen ''Point2d'' zeigt und ''pp3'' auf einen ''Point3d''. ''pp2'' wird zweimal der Wert von ''pp3'' zugewiesen, einmal mit einem Static-Cast und einmal mit einem C-Style-Cast. Der Static-Cast wirft dabei folgende Meldung: error: invalid static_cast from type `Point3d*' to type `Point2d*' Der Hinweis ist korrekt, denn ''Point3d'' und ''Point2d'' stehen in keinerlei Verbindung zueinander. Die Zeiger dürfen also nicht einfach ausgetauscht werden. Beim C-Style-Cast, wird der Zeigeraustausch akzeptiert nach dem Motto "Zeiger ist Zeiger". ''static_cast'' liefert hier also den Hinweis, dass man gerade im Begriff ist, Mist zu bauen, während der C-Style-Cast hier jegliche Warnung unterdrückt. Mit ''static_cast'' lassen sich Zeiger innerhalb der Objekthierarchie casten: Folgendes Beispiel: class Tier { }; class Hund : public Tier { }; int main(int argc, char **argv) { Hund * hund = new Hund(); Tier * tier; tier = hund; Da der Hund ein Tier ist, muss hier kein Cast durchgeführt werden, denn ein Hund verhält sich ja genauso, wie ein Tier. Aber im umgekehrten Fall, muss der Compiler die Anweisung ''hund = tier'' ablehnen, da eine Katze ja auch ein Tier wäre, und wenn das Tier eine Katze ist, so würde diese Anweisung ja bedeuten, dass der Zeiger auf den Hund in Wirklichkeit auf eine Katze zeigt. Das darf natürlich nicht sein. Das letzte Wort in der Programmierung hat jedoch immer der Entwickler. Und da Hund und Tier in einer Hierarchie stehen, ist der expliziten Aussage, dass ein Tier definitiv ein Hund ist, nichts entgegen zu bringen. Wenn dem nicht so ist, stürzt das Programm eventuell ab, aber wenn der Entwickler sich sicher ist, so hat er nunmal das letzte Wort. Dies kann der Entwickler mit einem ''static_cast'' erzwingen: Hund * meinHund; meinHund = static_cast< Hund * >( tier ); return 0; } Dieser Quelltext wird vom C++-Compiler so akzeptiert. Die Aussage lautet 'Ich, der Entwickler, weiß zu 100%, dass das Objekt, auf das ''tier'' zeigt, ein Hund ist.'.