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