Rzuty i konwersje
Transkrypt
Rzuty i konwersje
Rzuty i konwersje Bogdan Kreczmer ZPCiR IIAiR PWr pokój 307 budynek C3 [email protected] c 2005–2011 Bogdan Kreczmer? Copyright ? Niniejszy dokument zawiera materiały do wykładu na temat programowania obiektowego. Jest on udostepniony ˛ pod warunkiem wykorzystania wyłacznie ˛ do własnych prywatnych potrzeb i może on być kopiowany wyłacznie ˛ w całości, razem z niniejsza˛ strona˛ tytułowa. ˛ 1 Domyślne konwersje typów struct Wektor2f { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float x, y; Wektor2f( float x=0, float y=0 ): x(x), y(y) { } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . struct LZespolona { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float re, im; LZespolona( ): re( ), im( ) { } // W ten sposób pola re i im bed ˛ a˛ miały wartość 0. }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . void Funkcja dla Wektora( Wektor2f const & W ) { . . . } int main( ) { LZespolona Wektor2f Z; ( hh( ( hZ; h ( h W(h =( //Tu jest bład. ˛ Brak zgodności typów. ( h(h ((h h Z ();h Funkcja dla Wektora( ... h ( //Tu także jest bład ˛ z tego samego powodu. Rzuty i konwersje 1 Domyślne konwersje typów struct Wektor2f { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float x, y; Wektor2f( float x=0, float y=0 ): x(x), y(y) { } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . struct LZespolona { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float re, im; LZespolona( ): re( ), im( ) { } // W ten sposób pola re i im bed ˛ a˛ miały wartość 0. }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . void Funkcja dla Wektora( Wektor2f const & W ) { . . . } int main( ) { LZespolona Wektor2f Z; ( hh( ( hZ; h ( h W(h =( //Tu jest bład. ˛ Brak zgodności typów. ( h(h ((h h Z ();h Funkcja dla Wektora( ... h ( //Tu także jest bład ˛ z tego samego powodu. Ze wzgledu ˛ na wymóg statycznej zgodności typów powyższe operacje sa˛ niepoprawne. Niezgodności typów wykrywane sa˛ w trakcie kompilacji. Rzuty i konwersje 2 Domyślne konwersje typów ... struct Wektor2f { //. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float x, y; Wektor2f( LZespolona const& Z ); // Ten konstruktor umożliwia konwersje typów. Wektor2f( float x=0, float y=0 ): x(x), y(y) { } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ... Wektor2f::Wektor2f( LZespolona const& Z ): x(Z. re ), y(Z. im ) { } void Funkcja dla Wektora( const Wektor2f & W ) { . . . } int main( ) { LZespolona Wektor2f Z; W = Z; Funkcja dla Wektora( Z ); ... // Tu jest dobrze gdyż możliwa jest niejawna konwersja. // Tu jest dobrze. Rzuty i konwersje 2 Domyślne konwersje typów ... struct Wektor2f { //. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float x, y; Wektor2f( LZespolona const& Z ); // Ten konstruktor umożliwia konwersje typów. Wektor2f( float x=0, float y=0 ): x(x), y(y) { } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ... Wektor2f::Wektor2f( LZespolona const& Z ): x(Z. re ), y(Z. im ) { } void Funkcja dla Wektora( const Wektor2f & W ) { . . . } int main( ) { LZespolona Wektor2f Z; W = Z; Funkcja dla Wektora( Z ); ... // Tu jest dobrze gdyż możliwa jest niejawna konwersja. // Tu jest dobrze. Dodatkowy konstruktor daje przepis jak z obiektu klasy Wektor2f stworzyć obiekt klasy LZespolona. Dlatego kompilator może sam rozwiazać ˛ napotkany problem niezgodności typów. Rzuty i konwersje 3 Bezpieczne rzutowanie struct Wektor2f { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float x, y; Wektor2f( const LZespolona & Z ); // Ten konstruktor umożliwia konwersje typów. Wektor2f( float x=0, float y=0 ): x(x), y(y) { } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ... Wektor2f::Wektor2f( const LZespolona & Z ): x(Z. re ), y(Z. im ) { } int main( ) { LZespolona Z; Wektor2f W1 = Z; Wektor2f W2 = Wektor2f(Z); Wektor2f W3 = static cast<Wektor2f>(Z); } // Tu jest dobrze gdyż możliwa jest niejawna konwersja. // Jawna konwersja poprzez wywołanie konstruktora. // Jawna konwersja poprzez bezpieczne rzutowanie. Rzuty i konwersje 3 Bezpieczne rzutowanie struct Wektor2f { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float x, y; Wektor2f( const LZespolona & Z ); // Ten konstruktor umożliwia konwersje typów. Wektor2f( float x=0, float y=0 ): x(x), y(y) { } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ... Wektor2f::Wektor2f( const LZespolona & Z ): x(Z. re ), y(Z. im ) { } int main( ) { LZespolona Z; Wektor2f W1 = Z; Wektor2f W2 = Wektor2f(Z); Wektor2f W3 = static cast<Wektor2f>(Z); } // Tu jest dobrze gdyż możliwa jest niejawna konwersja. // Jawna konwersja poprzez wywołanie konstruktora. // Jawna konwersja poprzez bezpieczne rzutowanie. Rzutowanie typu obiektu można dokonać w sposób jawny i bezpieczny za pomoca˛ operatora static cast. Stosowanie tego operatora jest zalecane zamiast realizacji niejawnych rzutowań. Rzuty i konwersje 4 Domyślne konwersje typów struct Wektor2f { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float x, y; // Znowu zmiana explicit Wektor2f( LZespolona const& Z ); Wektor2f( float x=0, float y=0 ): x(x), y(y) { } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ... Wektor2f::Wektor2f( LZespolona const& Z ): x(Z. re ), y(Z. im ) { } void Funkcja dla Wektora( const Wektor2f & W ) { . . . } int main( ) { LZespolona Wektor2f Z; ( hh( ( hZ; h ( h W(h =( // Tu jest źle gdyż niejawna konwersja jest teraz zabroniona. Funkcja dla Wektora( Wektor2f( Z ) ); // Tu jest dobrze, gdyż konwersja jest wykonana w sposób jawny. Funkcja dla Wektora( static cast<Wektor2f>( Z ) ); // Najlepszy sposób, konwersja poprzez jawne rzutowanie. } Rzuty i konwersje 4 Domyślne konwersje typów struct Wektor2f { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float x, y; // Znowu zmiana explicit Wektor2f( LZespolona const& Z ); Wektor2f( float x=0, float y=0 ): x(x), y(y) { } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ... Wektor2f::Wektor2f( LZespolona const& Z ): x(Z. re ), y(Z. im ) { } void Funkcja dla Wektora( const Wektor2f & W ) { . . . } int main( ) { LZespolona Wektor2f Z; ( hh( ( hZ; h ( h W(h =( // Tu jest źle gdyż niejawna konwersja jest teraz zabroniona. Funkcja dla Wektora( Wektor2f( Z ) ); // Tu jest dobrze, gdyż konwersja jest wykonana w sposób jawny. Funkcja dla Wektora( static cast<Wektor2f>( Z ) ); // Najlepszy sposób, konwersja poprzez jawne rzutowanie. } Niejawna konwersja nie zawsze jest pożadana. ˛ Czasami może być źródłem błedów ˛ bardzo trudnych do wykrycia. Słowo kluczowe explicit pozwala zabronić niejawnych wywołań wybranych konstruktorów, tym samym konwersji z zastosowaniem tego konstruktora. Rzuty i konwersje 5 Konwertery struct Wektor2f { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float x, y; Wektor2f( float x=0, float y=0 ): x(x), y(y) { } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . struct LZespolona { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . re, im; float operator Wektor2f ( ) { return Wektor2f( re, im); } // Deklaracja i implementacja konwertera }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . void Funkcja dla Wektora( const Wektor2f & W ) { . . . } int main( ) { LZespolona Wektor2f Z; W = Z; // Konwersje moga˛ wykonywać sie˛ niejawnie // lub też można je wykonywać w sposób jawny (w stylu C) Funkcja dla Wektora( (Wektor2f ) Z ); Funkcja dla Wektora( static cast<Wektor2f>( Z ) ); // Najlepszy sposób, konwersja poprzez jawne rzutowanie. } Rzuty i konwersje 5 Konwertery struct Wektor2f { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float x, y; Wektor2f( float x=0, float y=0 ): x(x), y(y) { } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . struct LZespolona { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . re, im; float operator Wektor2f ( ) { return Wektor2f( re, im); } // Deklaracja i implementacja konwertera }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . void Funkcja dla Wektora( const Wektor2f & W ) { . . . } int main( ) { LZespolona Wektor2f Z; W = Z; // Konwersje moga˛ wykonywać sie˛ niejawnie // lub też można je wykonywać w sposób jawny (w stylu C) Funkcja dla Wektora( (Wektor2f ) Z ); Funkcja dla Wektora( static cast<Wektor2f>( Z ) ); // Najlepszy sposób, konwersja poprzez jawne rzutowanie. } Operatory konwersji dostarczaja˛ bardziej ogólnego mechanizmu przekształcania typu niż konstruktory. Dla konwerterów nie można zabronić konwersji domyślnych. Rzuty i konwersje 6 Konwertery struct Wektor2f { //. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float x, y; Wektor2f( float x=0, float y=0 ): x(x), y(y) { } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . struct LZespolona { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float re, im; operator Wektor2f ( ) { return Wektor2f( re, im); } // Deklaracja i implementacja konwertera }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . void Fun Zmieniajaca Wek( Wektor2f &W ) { . . . } int main( ) { LZespolona Z; Fun Zmieniajaca Wek( Z ); ... // // Tu nie może być już zastosowany konwerter do klasy Wektor2f, gdyż w tym przypadku potrzebna jest konwersja do typu Wektor2f&. Rzuty i konwersje 6 Konwertery struct Wektor2f { //. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float x, y; Wektor2f( float x=0, float y=0 ): x(x), y(y) { } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . struct LZespolona { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float re, im; operator Wektor2f ( ) { return Wektor2f( re, im); } // Deklaracja i implementacja konwertera }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . void Fun Zmieniajaca Wek( Wektor2f &W ) { . . . } int main( ) { LZespolona Z; Fun Zmieniajaca Wek( Z ); ... // // Tu nie może być już zastosowany konwerter do klasy Wektor2f, gdyż w tym przypadku potrzebna jest konwersja do typu Wektor2f&. Wymaganie ścisłej zgodności typów zapobiega błednym ˛ domyślnym użyciom konwerterów. Rzuty i konwersje 7 Konwertery struct Wektor2f { //. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float x, y; Wektor2f( float x=0, float y=0 ): x(x), y(y) { } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . struct LZespolona { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float re, im; operator Wektor2f & ( ) { Wektor2f W( re, im); return W; } // Błedna ˛ implementacja }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . void Fun Zmieniajaca Wek( Wektor2f &W ) { . . . } int main( ) { LZespolona Z; Fun Zmieniajaca Wek( Z ); ... // Tu byłoby już wszystko dobrze gdyby konwerter był poprawnie zaimplementowany. Rzuty i konwersje 7 Konwertery struct Wektor2f { //. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float x, y; Wektor2f( float x=0, float y=0 ): x(x), y(y) { } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . struct LZespolona { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float re, im; operator Wektor2f & ( ) { Wektor2f W( re, im); return W; } // Błedna ˛ implementacja }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . void Fun Zmieniajaca Wek( Wektor2f &W ) { . . . } int main( ) { LZespolona Z; Fun Zmieniajaca Wek( Z ); ... // Tu byłoby już wszystko dobrze gdyby konwerter był poprawnie zaimplementowany. Wszystkie uwagi odnoszace ˛ sie˛ do zwracanych wartości (obiektów, referencji itd.) przez metody odnosza˛ sie˛ również do konwerterów. Konwerter do referencji danej klasy nie może zwracać referencji do obiektu lokalnego. Rzuty i konwersje 8 Konwertery struct Wektor2f { float x, y; }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . struct LZespolona { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Wektor2f Wek; float & re, & im; LZespolona( ): re( Wek. x ), im( Wek. y ) { } // Deklaracja konwertera operator Wektor2f & ( ) { return Wek; } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . void Fun Zmieniajaca Wek( Wektor2f &W ) { . . . } int main( ) { LZespolona Wektor2f Z; W = Z; // Podstawienie jest poprawne dzieki ˛ niejawnej konwersji. ( hhh h( (( h (Z h =( W; // To podstawienie jest błedne. ˛ Nie jest możliwa domyślna konwersja po prawej stronie operatora. static cast<Wektor2f &>(Z) = W; // Tutaj jest już wszystko dobrze. Fun Zmieniajaca Wek( Z ); // Dzieki ˛ konwerterowi przekazanie parametru jest poprawne. ... Rzuty i konwersje 8 Konwertery struct Wektor2f { float x, y; }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . struct LZespolona { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Wektor2f Wek; float & re, & im; LZespolona( ): re( Wek. x ), im( Wek. y ) { } // Deklaracja konwertera operator Wektor2f & ( ) { return Wek; } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . void Fun Zmieniajaca Wek( Wektor2f &W ) { . . . } int main( ) { LZespolona Wektor2f Z; W = Z; // Podstawienie jest poprawne dzieki ˛ niejawnej konwersji. ( hhh h( (( h (Z h =( W; // To podstawienie jest błedne. ˛ Nie jest możliwa domyślna konwersja po prawej stronie operatora. static cast<Wektor2f &>(Z) = W; // Tutaj jest już wszystko dobrze. Fun Zmieniajaca Wek( Z ); // Dzieki ˛ konwerterowi przekazanie parametru jest poprawne. ... Konwersja do referencji musi odnosić sie˛ do obiektu znajdujacego ˛ sie˛ w obrebie ˛ zakresu definicji danego konwertera (dotyczy to ogólnie wszystkich metod i funkcji). Rzuty i konwersje 9 Konwertery struct LZespolona { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float x, y; operator float ( ) const{ return x ∗ x + y ∗ y; } }; //. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . int main() { LZespolona Z; float d = Z; if ( Z ) { cout << ”Różne od zera” d = d + Z - 5; ... // // << endl; } // To jest poprawne dzieki ˛ domyślnej konwersji do float. // To także jest poprawne. Konwersja w tym miejscu być może nie jest pożadana ˛ w przypadku, gdy zmienna˛ Z użyto przez pomyłk˛e. Rzuty i konwersje 9 Konwertery struct LZespolona { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float x, y; operator float ( ) const{ return x ∗ x + y ∗ y; } }; //. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . int main() { LZespolona Z; float d = Z; if ( Z ) { cout << ”Różne od zera” d = d + Z - 5; ... // // << endl; } // To jest poprawne dzieki ˛ domyślnej konwersji do float. // To także jest poprawne. Konwersja w tym miejscu być może nie jest pożadana ˛ w przypadku, gdy zmienna˛ Z użyto przez pomyłk˛e. Operatory konwersji zapewniaja˛ konwersje˛ danej klasy do dowolnego typu wbudowanego. Nadmierne korzystanie z tego mechanizmu może jednak być niebezpieczne. Rzuty i konwersje 10 Konwertery struct LZespolona { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float x, y; operator float ( ) const{ return x ∗ x + y ∗ y; } operator bool( ) const{ return x || y; } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . int main() { LZespolona Z; float d = Z; if ( Z ) { cout << ”Różne od zera” << endl; } // To jest poprawne dzieki ˛ domyślnej konwersji do float. // Teraz jest tu użyta konwersja do typu bool. h h-( ( h h d=( dh +( Z( 5; // W tym przypadku konwersja staje sie˛ niejednoznaczna dlatego wyrażenie to nie jest akceptowalne. d = d + static cast<float>(Z) - 5; // Należy dokonać jawnej konwersji aby wszystko było dobrze. ... h ( Rzuty i konwersje 10 Konwertery struct LZespolona { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float x, y; operator float ( ) const{ return x ∗ x + y ∗ y; } operator bool( ) const{ return x || y; } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . int main() { LZespolona Z; float d = Z; if ( Z ) { cout << ”Różne od zera” << endl; } // To jest poprawne dzieki ˛ domyślnej konwersji do float. // Teraz jest tu użyta konwersja do typu bool. h h-( ( h h d=( dh +( Z( 5; // W tym przypadku konwersja staje sie˛ niejednoznaczna dlatego wyrażenie to nie jest akceptowalne. d = d + static cast<float>(Z) - 5; // Należy dokonać jawnej konwersji aby wszystko było dobrze. ... h ( Zdefiniowanie konwersji do kilku typów podstawowych może prowadzić do niejednoznaczności. Może to być pożyteczne, gdyż zabezpiecza przed przypadkowym użyciem danej zmiennej w innym kontekście. Rzuty i konwersje 11 Konwertery Zapis operacji odczytu z cin można znacznie uprościć. Jest to możliwe dzieki ˛ temu, że dla klasy std::istream przeciażenie ˛ operatora >> zwraca referencje˛ do std::istream (obiekt zwraca referencje˛ do samego siebie) oraz zdefiniowany jest operator konwersji do typu void*, który zwraca zanegowany wynik metody fail. float d; do { cin >> d; if ( cin.fail( ) ) break ; ... } while (true); Rzuty i konwersje 11 Konwertery Zapis operacji odczytu z cin można znacznie uprościć. Jest to możliwe dzieki ˛ temu, że dla klasy std::istream przeciażenie ˛ operatora >> zwraca referencje˛ do std::istream (obiekt zwraca referencje˛ do samego siebie) oraz zdefiniowany jest operator konwersji do typu void*, który zwraca zanegowany wynik metody fail. float d; float d; do { cin >> d; if ( cin.fail( ) ) break ; ... } while (true); while (!(cin >> d).fail( ) ) { ... } =⇒ Rzuty i konwersje 11 Konwertery Zapis operacji odczytu z cin można znacznie uprościć. Jest to możliwe dzieki ˛ temu, że dla klasy std::istream przeciażenie ˛ operatora >> zwraca referencje˛ do std::istream (obiekt zwraca referencje˛ do samego siebie) oraz zdefiniowany jest operator konwersji do typu void*, który zwraca zanegowany wynik metody fail. float d; float d; do { cin >> d; if ( cin.fail( ) ) break ; ... } while (true); while (!(cin >> d).fail( ) ) { ... } =⇒ ⇓ float d; while (cin >> d) { ... } // // Wykonuj dopóki poprawnie odczytasz wartość typu float Rzuty i konwersje 12 Czytanie z pliku #include <iostream> #include <fstream> #include <iostream> #include <fstream> using namespace std; using namespace std; int main( ) { ifstream Strm; char Znak; int main( ) { ifstream Strm( ”plik.txt” ); char Znak; if ( !Strm ) return 1; Strm.open( ”plik.txt” ); if ( !Strm.is open( ) ) return 1; Strm >> noskipws >> Znak; while ( Strm.good( ) ) { cout << Znak; Strm >> Znak; } Strm.close( ); Strm >> noskipws; while ( Strm >>Znak ) { cout << Znak; } } } Rzuty i konwersje 12 Czytanie z pliku #include <iostream> #include <fstream> #include <iostream> #include <fstream> using namespace std; using namespace std; int main( ) { ifstream Strm; char Znak; int main( ) { ifstream Strm( ”plik.txt” ); char Znak; if ( !Strm ) return 1; Strm.open( ”plik.txt” ); if ( !Strm.is open( ) ) return 1; Strm >> noskipws >> Znak; while ( Strm.good( ) ) { cout << Znak; Strm >> Znak; } Strm.close( ); Strm >> noskipws; while ( Strm >>Znak ) { cout << Znak; } } } Zdefiniowany konwerter do typu void∗ oraz przeciażenie ˛ operatora ! pozwalaja˛ uprościć zapis operacji sprawdzania stanu strumienia. Rzuty i konwersje 13 Operatory rzutowania ˛ modyfikatory const oraz volatile. const cast – rzutowanie usuwajace Całość konwersji realizowana jest przez kompilator. Rzutowanie to należy do rzutowań bezpiecznych. static cast – używany do zdefiniowanych przez użytkownika, standardowych lub niejawnych konwersji typów. Całość konwersji realizowana jest przez kompilator. Rzutowanie to należy do rzutowań w zasadzie bezpiecznych. reinterpret cast – jest najbardziej niebezpiecznym rzutowaniem. Wykonuje on konwersje˛ miedzy ˛ wskaźnikami oraz wskaźnikami i liczbami. Źle przeprowadzone rzutowanie może być źródłem błe˛ dów trudnych do wykrycia. dynamic cast – obsługuje tylko obiekty klas polimorficznych. Zapewnia realizacje˛ rzutowania “w góre”. ˛ Rzutowanie to należy do rzutowań bezpiecznych. Rzuty i konwersje 14 Operator const cast< > class LZespolona { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . public : float re, im; void Zmien( float re, float im ) { re = re; im = im; } float Re( ) const { return re; } float Im( ) const { return im; } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . int main( ) { const LZespolona LZespolona StaleZ; ZmienZ; const cast<LZespolona&>(StaleZ).Zmien(2,3); hhhh (( h( ( h( hhh >(StaleZ).Zmien(2,3); const cast<LZespolona (((( const cast<const LZespolona&>(ZmienZ).Re( ); } Rzuty i konwersje 14 Operator const cast< > class LZespolona { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . public : float re, im; void Zmien( float re, float im ) { re = re; im = im; } float Re( ) const { return re; } float Im( ) const { return im; } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . int main( ) { const LZespolona LZespolona StaleZ; ZmienZ; const cast<LZespolona&>(StaleZ).Zmien(2,3); hhhh (( h( ( h( hhh >(StaleZ).Zmien(2,3); const cast<LZespolona (((( const cast<const LZespolona&>(ZmienZ).Re( ); } Rzutowanie z wykorzystaniem operatora const cast może być tylko rzutowaniem na wskaźnik lub referencje. ˛ Może ono usuwać lub dodawać modyfikator const. Rzuty i konwersje 15 Operator const cast< > int main( ) { const int Stala = 5; volatile int ZmUlotna; const cast<int &>(Stala) = 5; const cast<int &>(ZmUlotna) = 5; ( ( hh (h (h ((= h const cast<const int &>(ZmUlotna)h 5; const cast<volatile int &>(Stala) = 5; ( hh( h( h( h> const cast <(int = 5; h(ZmUlotna) h ((( hh ( const char Tab[ ] = ”łódka”; const cast<char ∗>( Tab )[ 0 ] = ’w’; ∗const cast<char ∗>( Tab ) = ’w’; } Rzuty i konwersje 15 Operator const cast< > int main( ) { const int Stala = 5; volatile int ZmUlotna; const cast<int &>(Stala) = 5; const cast<int &>(ZmUlotna) = 5; ( ( hh (h (h ((= h const cast<const int &>(ZmUlotna)h 5; const cast<volatile int &>(Stala) = 5; ( hh( h( h( h> const cast <(int = 5; h(ZmUlotna) h ((( hh ( const char Tab[ ] = ”łódka”; const cast<char ∗>( Tab )[ 0 ] = ’w’; ∗const cast<char ∗>( Tab ) = ’w’; } Przy rzutowaniu za pomoca˛ const cast sa˛ te same zasady do modyfikator const jak też modyfikatora volatile. Rzuty i konwersje 16 Operator static cast< > int main( ) { unsigned int ZmBezZnaku = 5; int ZmZeZnakiem = –50; ZmBezZnaku = static cast<unsigned int >(ZmZeZnakiem); ( hh( h (h( h h int &( >(ZmBezZnaku) = ZmZeZnakiem; static cast<( hh( h( h( h static cast( <(int >(h (ZmBezZnaku) = ZmZeZnakiem; double ZmDouble = numeric limits<double >::max( ); float ZmFloat = static cast<float >(ZmDouble); cout << ”ZmDouble: ” << ZmDouble << endl; cout << ”ZmFloat: ” << ZmFloat << endl; cout << ”ZmBezZnaku: ” << ZmBezZnaku << endl; } Rzuty i konwersje 16 Operator static cast< > int main( ) { unsigned int ZmBezZnaku = 5; int ZmZeZnakiem = –50; ZmBezZnaku = static cast<unsigned int >(ZmZeZnakiem); ( hh( h (h( h h int &( >(ZmBezZnaku) = ZmZeZnakiem; static cast<( hh( h( h( h static cast( <(int >(h (ZmBezZnaku) = ZmZeZnakiem; double ZmDouble = numeric limits<double >::max( ); float ZmFloat = static cast<float >(ZmDouble); } cout << ”ZmDouble: ” << ZmDouble << endl; Wynik działania: cout << ”ZmFloat: ” << ZmFloat << endl; ZmDouble: 1.79769e+308 cout << ”ZmBezZnaku: ” << ZmBezZnaku << endl; ZmFloat: inf ZmBezZnaku: 4294967246 Rzuty i konwersje 16 Operator static cast< > int main( ) { unsigned int ZmBezZnaku = 5; int ZmZeZnakiem = –50; ZmBezZnaku = static cast<unsigned int >(ZmZeZnakiem); ( hh( h (h( h h int &( >(ZmBezZnaku) = ZmZeZnakiem; static cast<( hh( h( h( h static cast( <(int >(h (ZmBezZnaku) = ZmZeZnakiem; double ZmDouble = numeric limits<double >::max( ); float ZmFloat = static cast<float >(ZmDouble); } cout << ”ZmDouble: ” << ZmDouble << endl; Wynik działania: cout << ”ZmFloat: ” << ZmFloat << endl; ZmDouble: 1.79769e+308 cout << ”ZmBezZnaku: ” << ZmBezZnaku << endl; ZmFloat: inf ZmBezZnaku: 4294967246 W przypadku typów wbudowanych nie można rzutować na referencje. ˛ Jest to możliwe w przypadku klas, dla których zdefiniowane zostały odpowiednie konwertery. Jeżeli rzutowanie realizowane jest “na wartość”, to produkt rzutowania nie jest l-wartościa. ˛ Rzuty i konwersje 17 Operator static cast< > int main( ) { int ZmInt = 32111; double ZmDouble = ZmInt; ZmDouble = ZmInt; ZmDouble = static cast <double >(ZmInt); // Domyślne rzutowanie // Rzutowanie jawne ZmDouble = 6.25; ZmInt = ZmDouble; ZmInt = static cast <int >(ZmDouble); // Tu kompilator bedzie ˛ ostrzegał // Tu już nie cout << ”ZmInt: ” << ZmInt << endl; } Rzuty i konwersje 17 Operator static cast< > int main( ) { int ZmInt = 32111; double ZmDouble = ZmInt; ZmDouble = ZmInt; ZmDouble = static cast <double >(ZmInt); // Domyślne rzutowanie // Rzutowanie jawne ZmDouble = 6.25; ZmInt = ZmDouble; ZmInt = static cast <int >(ZmDouble); // Tu kompilator bedzie ˛ ostrzegał // Tu już nie cout << ”ZmInt: ” << ZmInt << endl; } Wynik kompilacji: jk@noxon PRG> g++ konwersja.cpp konwersja.cpp: In function ‘int main()’: konwersja.cpp:7: warning: converting to ’int’ from ’double’ Wynik działania: ZmInt: 6 Rzuty i konwersje 17 Operator static cast< > int main( ) { int ZmInt = 32111; double ZmDouble = ZmInt; ZmDouble = ZmInt; ZmDouble = static cast <double >(ZmInt); // Domyślne rzutowanie // Rzutowanie jawne ZmDouble = 6.25; ZmInt = ZmDouble; ZmInt = static cast <int >(ZmDouble); // Tu kompilator bedzie ˛ ostrzegał // Tu już nie cout << ”ZmInt: ” << ZmInt << endl; } Wynik kompilacji: jk@noxon PRG> g++ konwersja.cpp konwersja.cpp: In function ‘int main()’: konwersja.cpp:7: warning: converting to ’int’ from ’double’ Wynik działania: ZmInt: 6 W przypadku rzutowań stratnych należy rzutowania dokonywać w sposób jawny. W ten sposób eliminujemy zbedne ˛ komunikaty i poprawiamy czytelność programu. Nie wszystkie kompilatory domyślnie ostrzegaja˛ przed stratnymi rzutowaniami. Rzuty i konwersje 18 Operator static cast< > int main( ) { short int ZmSInt Pomocnicza = 5; short int ZmSInt = 32111; double ZmDouble = ZmSInt; void ∗wZm = &ZmSInt; ∗static cast <double ∗>(wZm) = ZmDouble; short int ∗wZmSInt = &ZmSInt; ( ( hhhh hhh ( (( (h h ((( (((h h∗>(wZmSInt) h ∗static cast <double = ZmDouble; // To kompilator wykryje cout << ” ZmSInt Pomocnicza: ” << ZmSInt Pomocnicza << endl; cout << ” ZmSInt ” << ZmSInt << endl; cout << ” ZmDouble: ” << ZmDouble << endl; } Rzuty i konwersje 18 Operator static cast< > int main( ) { short int ZmSInt Pomocnicza = 5; short int ZmSInt = 32111; double ZmDouble = ZmSInt; void ∗wZm = &ZmSInt; ∗static cast <double ∗>(wZm) = ZmDouble; short int ∗wZmSInt = &ZmSInt; ( ( hhhh hhh ( (( (h h ((( (((h h∗>(wZmSInt) h ∗static cast <double = ZmDouble; // To kompilator wykryje cout << ” ZmSInt Pomocnicza: ” << ZmSInt Pomocnicza << endl; cout << ” ZmSInt ” << ZmSInt << endl; cout << ” ZmDouble: ” << ZmDouble << endl; } Wynik działania: ZmSInt Pomocnicza: ZmSInt: ZmDouble: 0 0 32111 Rzuty i konwersje 18 Operator static cast< > int main( ) { short int ZmSInt Pomocnicza = 5; short int ZmSInt = 32111; double ZmDouble = ZmSInt; void ∗wZm = &ZmSInt; ∗static cast <double ∗>(wZm) = ZmDouble; short int ∗wZmSInt = &ZmSInt; ( ( hhhh hhh ( (( (h h ((( (((h h∗>(wZmSInt) h ∗static cast <double = ZmDouble; // To kompilator wykryje cout << ” ZmSInt Pomocnicza: ” << ZmSInt Pomocnicza << endl; cout << ” ZmSInt ” << ZmSInt << endl; cout << ” ZmDouble: ” << ZmDouble << endl; } Wynik działania: ZmSInt Pomocnicza: ZmSInt: ZmDouble: 0 0 32111 Wykorzystywanie wskaźników na typ void może prowadzić do bardzo niebezpiecznych konstrukcji. Z tego wzgledu ˛ należy unikać tego typu rozwiaza ˛ ń. Rzuty i konwersje 19 Operator reinterpret cast< > class KlasaInt2 { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . public : int Pole1, Pole2; }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . int main( ) { KlasaInt2 Ob; int Zm = 30; Ob. Pole1 = 100; Ob. Pole2 = 200; int ∗Tab = reinterpret cast <int ∗>(&Ob); cout << ”Tab[0]: ” << Tab[0] << endl; cout << ”Tab[1]: ” << Tab[1] << endl; KlasaInt2 &ZlyOb = reinterpret cast <KlasaInt2 &>(Zm); } cout << ”ZlyOb. Pole1: ” << ZlyOb. Pole1 << endl; cout << ”ZlyOb. Pole2: ” << ZlyOb. Pole2 << endl; Rzuty i konwersje 19 Operator reinterpret cast< > class KlasaInt2 { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . public : int Pole1, Pole2; }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . int main( ) { KlasaInt2 Ob; int Zm = 30; Ob. Pole1 = 100; Ob. Pole2 = 200; int ∗Tab = reinterpret cast <int ∗>(&Ob); cout << ”Tab[0]: ” << Tab[0] << endl; cout << ”Tab[1]: ” << Tab[1] << endl; KlasaInt2 &ZlyOb = reinterpret cast <KlasaInt2 &>(Zm); } cout << ”ZlyOb. Pole1: ” << ZlyOb. Pole1 << endl; cout << ”ZlyOb. Pole2: ” << ZlyOb. Pole2 << endl; Wynik działania: Tab[0]: 100 Tab[1]: 200 ZlyOb. Pole1: ZlyOb. Pole2: 30 100 Rzuty i konwersje 19 Operator reinterpret cast< > class KlasaInt2 { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . public : int Pole1, Pole2; }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . int main( ) { KlasaInt2 Ob; int Zm = 30; Ob. Pole1 = 100; Ob. Pole2 = 200; int ∗Tab = reinterpret cast <int ∗>(&Ob); cout << ”Tab[0]: ” << Tab[0] << endl; cout << ”Tab[1]: ” << Tab[1] << endl; KlasaInt2 &ZlyOb = reinterpret cast <KlasaInt2 &>(Zm); } cout << ”ZlyOb. Pole1: ” << ZlyOb. Pole1 << endl; cout << ”ZlyOb. Pole2: ” << ZlyOb. Pole2 << endl; Wynik działania: Tab[0]: 100 Tab[1]: 200 ZlyOb. Pole1: ZlyOb. Pole2: 30 100 Stosowanie operatora reinterpret cast daje możliwości swobodnego manipulowania organizacja˛ pamieci. ˛ Jednak stwarza to potencjalnie duże niebezpieczeństwo wystapienia ˛ bardzo poważnych błedów. ˛ Powoduje również, że program zazwyczaj staje sie˛ nieprzenośny. Rzuty i konwersje 20 Rzutowanie w góre˛ Rzuty i konwersje 20 Rzutowanie w góre˛ Rzutowanie ”w góre” ˛ jest rzutowaniem na klase˛ bazowa. ˛ Tego typu rzutowanie zawsze sie˛ powiedzie, gdyż obiekt klasy pochodnej musi zawierać podobiekt klasy bazowej. Z tego powodu rzutowanie to może być realizowane niejawnie. Rzuty i konwersje 21 Operator dynamic cast< > class KlasaBazowa { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . public : virtual ∼ KlasaBazowa( ) { } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . class KlasaPochodna: public KlasaBazowa { }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . int main( ) { KlasaBazowa KlasaPochodna KlasaBazowa ∗wObBazo; ObPoch; &ObBazo = ObPoch; wObBazo = &ObPoch; wObBazo = dynamic cast<KlasaBazowa∗>(&ObPoch); // Niejawne rzutowanie referencji // Jawne rzutowanie ”w góre” ˛ } Rzuty i konwersje 21 Operator dynamic cast< > class KlasaBazowa { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . public : virtual ∼ KlasaBazowa( ) { } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . class KlasaPochodna: public KlasaBazowa { }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . int main( ) { KlasaBazowa KlasaPochodna KlasaBazowa ∗wObBazo; ObPoch; &ObBazo = ObPoch; wObBazo = &ObPoch; wObBazo = dynamic cast<KlasaBazowa∗>(&ObPoch); // Niejawne rzutowanie referencji // Jawne rzutowanie ”w góre” ˛ } Rzutowanie ”w góre” ˛ jest rzutowaniem całkowicie bezpiecznym. Rzutowanie to może odnosić sie˛ zarówno do wskaźników jak też do referencji. Może być również wykonywane niejawne na parametrach wywołania funkcji lub metod. Rzuty i konwersje 22 Niejawne rzutowanie ”w góre” ˛ class KlasaBazowa { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . public : virtual ∼ KlasaBazowa( ) { } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . class KlasaPochodna: public KlasaBazowa { }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . void Funkcja( KlasaBazowa & Ob ) { ... } int main( ) { KlasaPochodna ObPoch; Funkcja( ObPoch ); // Tu nastepuje ˛ niejawne rzutowanie w góre. ˛ } Rzuty i konwersje 22 Niejawne rzutowanie ”w góre” ˛ class KlasaBazowa { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . public : virtual ∼ KlasaBazowa( ) { } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . class KlasaPochodna: public KlasaBazowa { }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . void Funkcja( KlasaBazowa & Ob ) { ... } int main( ) { KlasaPochodna ObPoch; Funkcja( ObPoch ); // Tu nastepuje ˛ niejawne rzutowanie w góre. ˛ } W przypadku parametrów wywołania funkcji lub metod niejawne rzutowanie ”w góre” ˛ realizowane jest niezależnie od tego czy parametry te sa˛ modyfikowalne, czy też nie. Rzuty i konwersje 23 Rzutowanie w dół Rzuty i konwersje 23 Rzutowanie w dół Rzutowanie ”w dół” jest rzutowaniem na klase˛ pochodna. ˛ Ze wzgledu ˛ na to, że obiekt klasy bazowej nie zawsze musi być składnikiem obiektu klasy pochodnej (np. może wystepować ˛ samodzielnie), rzutowanie to może sie˛ nie powieść. Rzuty i konwersje 24 Operator dynamic cast< > class KlasaBazowa { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . public : virtual ∼ KlasaBazowa( ) { } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . class KlasaPochodna: public KlasaBazowa { }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . int main( ) { KlasaBazowa KlasaPochodna ObBazo, ∗wObBazoA = &ObBazo, ∗wObBazoB; ObPoch, ∗wObPoch; wObBazoB = &ObPoch; KlasaPochodna &rObPoch = dynamic cast <KlasaPochodna&>(∗wObBazoB); wObPoch = dynamic cast <KlasaPochodna∗>(wObBazoB); cout << ”Adres: ” << wObPoch << endl; wObPoch = dynamic cast <KlasaPochodna∗>(wObBazoA); cout << ”Adres: ” << wObPoch << endl; } Rzuty i konwersje 24 Operator dynamic cast< > class KlasaBazowa { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . public : virtual ∼ KlasaBazowa( ) { } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . class KlasaPochodna: public KlasaBazowa { }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . int main( ) { KlasaBazowa KlasaPochodna ObBazo, ∗wObBazoA = &ObBazo, ∗wObBazoB; ObPoch, ∗wObPoch; wObBazoB = &ObPoch; KlasaPochodna &rObPoch = dynamic cast <KlasaPochodna&>(∗wObBazoB); wObPoch = dynamic cast <KlasaPochodna∗>(wObBazoB); cout << ”Adres: ” << wObPoch << endl; wObPoch = dynamic cast <KlasaPochodna∗>(wObBazoA); cout << ”Adres: ” << wObPoch << endl; } Wynik działania: Adres: Adres: 0xbf9c0880 0 Rzuty i konwersje 24 Operator dynamic cast< > class KlasaBazowa { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . public : virtual ∼ KlasaBazowa( ) { } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . class KlasaPochodna: public KlasaBazowa { }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . int main( ) { KlasaBazowa KlasaPochodna ObBazo, ∗wObBazoA = &ObBazo, ∗wObBazoB; ObPoch, ∗wObPoch; wObBazoB = &ObPoch; KlasaPochodna &rObPoch = dynamic cast <KlasaPochodna&>(∗wObBazoB); wObPoch = dynamic cast <KlasaPochodna∗>(wObBazoB); cout << ”Adres: ” << wObPoch << endl; wObPoch = dynamic cast <KlasaPochodna∗>(wObBazoA); cout << ”Adres: ” << wObPoch << endl; } Wynik działania: Adres: Adres: 0xbf9c0880 0 Wykonujac ˛ rzutowanie ”w dół” jeśli obiekt klasy bazowej nie jest cz˛eścia˛ składowa˛ obiektu klasy pochodnej, w przypadku rzutowania wskaźnika otrzymywany jest adres NULL, zaś w przypadku rzutowania referencji zgłaszany jest wyjatek ˛ bad cast. Rzuty i konwersje 25 Operator dynamic cast< > class KlasaBazowa { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . public : virtual ∼ KlasaBazowa( ) { } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . class KlasaPochodna: public KlasaBazowa { }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . int main( ) { KlasaBazowa ObBazo, ∗wObBazoA = &ObBazo, ∗wObBazoB; KlasaPochodna ObPoch, ∗wObPoch; wObBazoB = &ObPoch; wObPoch = dynamic cast <KlasaPochodna∗>(wObBazoB); cout << ”Adres: ” << wObPoch << endl; wObPoch = static cast <KlasaPochodna∗>(wObBazoB); cout << ”Adres: ” << wObPoch << endl; wObPoch = static cast <KlasaPochodna∗>(wObBazoA); cout << ”Adres: ” << wObPoch << endl; } Rzuty i konwersje 25 Operator dynamic cast< > class KlasaBazowa { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . public : virtual ∼ KlasaBazowa( ) { } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . class KlasaPochodna: public KlasaBazowa { }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . int main( ) { KlasaBazowa ObBazo, ∗wObBazoA = &ObBazo, ∗wObBazoB; KlasaPochodna ObPoch, ∗wObPoch; wObBazoB = &ObPoch; wObPoch = dynamic cast <KlasaPochodna∗>(wObBazoB); cout << ”Adres: ” << wObPoch << endl; wObPoch = static cast <KlasaPochodna∗>(wObBazoB); cout << ”Adres: ” << wObPoch << endl; wObPoch = static cast <KlasaPochodna∗>(wObBazoA); cout << ”Adres: ” << wObPoch << endl; } Wynik działania: Adres: Adres: Adres: 0xbfa48210 0xbfa48210 0xbfa48230 Rzuty i konwersje 25 Operator dynamic cast< > class KlasaBazowa { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . public : virtual ∼ KlasaBazowa( ) { } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . class KlasaPochodna: public KlasaBazowa { }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . int main( ) { KlasaBazowa ObBazo, ∗wObBazoA = &ObBazo, ∗wObBazoB; KlasaPochodna ObPoch, ∗wObPoch; wObBazoB = &ObPoch; wObPoch = dynamic cast <KlasaPochodna∗>(wObBazoB); cout << ”Adres: ” << wObPoch << endl; wObPoch = static cast <KlasaPochodna∗>(wObBazoB); cout << ”Adres: ” << wObPoch << endl; wObPoch = static cast <KlasaPochodna∗>(wObBazoA); cout << ”Adres: ” << wObPoch << endl; } Wynik działania: Adres: Adres: Adres: 0xbfa48210 0xbfa48210 0xbfa48230 Wykorzystanie operatora dynamic cast wiaże ˛ sie˛ z dodatkowym narzutem. Zastosowanie w takich przypadkach operatora static cast daje bardziej efektywny kod, jednak jest bardziej niebezpieczne. Rzuty i konwersje 26 Podsumowanie • Operatory konwersji wraz z innymi operatorami dostarczaja˛ bardzo silnego mechanizmu, który pozwala traktować klasy definiowane przez programiste˛ na równi z typami wbudowanymi. Rzuty i konwersje 26 Podsumowanie • Operatory konwersji wraz z innymi operatorami dostarczaja˛ bardzo silnego mechanizmu, który pozwala traktować klasy definiowane przez programiste˛ na równi z typami wbudowanymi. • Nadużywanie mechanizmu domyślnych konwersji jest potencjalnie bardzo niebezpieczne, gdyż może prowadzić do błedów, ˛ które sa˛ bardzo trudno wykrywalne. Moga˛ one prowadzić do utraty kontroli nad przebiegiem obliczeń. Z tych powodów należy: Rzuty i konwersje 26 Podsumowanie • Operatory konwersji wraz z innymi operatorami dostarczaja˛ bardzo silnego mechanizmu, który pozwala traktować klasy definiowane przez programiste˛ na równi z typami wbudowanymi. • Nadużywanie mechanizmu domyślnych konwersji jest potencjalnie bardzo niebezpieczne, gdyż może prowadzić do błedów, ˛ które sa˛ bardzo trudno wykrywalne. Moga˛ one prowadzić do utraty kontroli nad przebiegiem obliczeń. Z tych powodów należy: ? definiować operatory konwersji tylko dla sytuacji jasno określonych nie prowadzacych ˛ do dwuznacznych interpretacji, tzn. sytuacji, w których czasami konwersja domyślna może być pożadana, ˛ zaś w innych nie. Rzuty i konwersje 26 Podsumowanie • Operatory konwersji wraz z innymi operatorami dostarczaja˛ bardzo silnego mechanizmu, który pozwala traktować klasy definiowane przez programiste˛ na równi z typami wbudowanymi. • Nadużywanie mechanizmu domyślnych konwersji jest potencjalnie bardzo niebezpieczne, gdyż może prowadzić do błedów, ˛ które sa˛ bardzo trudno wykrywalne. Moga˛ one prowadzić do utraty kontroli nad przebiegiem obliczeń. Z tych powodów należy: ? definiować operatory konwersji tylko dla sytuacji jasno określonych nie prowadzacych ˛ do dwuznacznych interpretacji, tzn. sytuacji, w których czasami konwersja domyślna może być pożadana, ˛ zaś w innych nie. ? wprowadzona domyślna konwersja nie powinna powodować znaczacego ˛ zwiekszenia ˛ przypadkowych podstawień. Rzuty i konwersje 26 Podsumowanie • Operatory konwersji wraz z innymi operatorami dostarczaja˛ bardzo silnego mechanizmu, który pozwala traktować klasy definiowane przez programiste˛ na równi z typami wbudowanymi. • Nadużywanie mechanizmu domyślnych konwersji jest potencjalnie bardzo niebezpieczne, gdyż może prowadzić do błedów, ˛ które sa˛ bardzo trudno wykrywalne. Moga˛ one prowadzić do utraty kontroli nad przebiegiem obliczeń. Z tych powodów należy: ? definiować operatory konwersji tylko dla sytuacji jasno określonych nie prowadzacych ˛ do dwuznacznych interpretacji, tzn. sytuacji, w których czasami konwersja domyślna może być pożadana, ˛ zaś w innych nie. ? wprowadzona domyślna konwersja nie powinna powodować znaczacego ˛ zwiekszenia ˛ przypadkowych podstawień. ? w przypadku definiowania konstruktorów jednoargumentowych, które nie sa˛ przewidziane do konwersji typów, należy je definiować jako explicit. Rzuty i konwersje 27 Pytania i ćwiczenia 1. Dany jest fragment kodu: struct Wektor2f { float x, y; Wektor2f(float w): x(w), y(w) { } operator float () const { return x” x + y” y; } Wektor2f operator + (Wektor2f W) const { W. x += x; W. y += y; return W; } }; int main( ) { Wektor2f Wek(2); float Liczba = 1; Liczba = 2 + Wek ∗ Liczba; ... (a) Jaka bedzie ˛ wartość zmiennej Liczba po wykonaniu powyższego wyrażenia arytmetycznego? (b) Które z metod klasy Wektor2f i ile razy zostana˛ uruchomione w trakcie wyliczania tego wyrażenia? (c) Czy wystapi ˛ niejawne wywołanie konstruktora klasy Wektor2f? 2. Dla przykładu jak powyżej rozważmy zamiast wcześniej przedstawionego wyrażenia, wyrażenie: Liczba += Wek = 2 + Wek ∗ Liczba; Należy odpowiedzieć na pytania przedstawione w poprzednim punkcie. Rzuty i konwersje