Zakres wa˙zno´sci, przekazywanie obiektów, obiekty tymczasowe
Transkrypt
Zakres wa˙zno´sci, przekazywanie obiektów, obiekty tymczasowe
Zakres ważności, przekazywanie obiektów, obiekty tymczasowe Bogdan Kreczmer ZPCiR IIAiR PWr pokój 307 budynek C3 [email protected] c 2005–2010 Bogdan Kreczmer? Copyright ° ? Niniejszy dokument zawiera materiały do wykładu na temat programowania obiektowego. Jest on udostepniony ˛ pod warun- kiem 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. ˛ Zakres ważności deklaracji class PrzykladKlasy { //. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . const char ∗ const Nazwa; public : PrzykladKlasy( const char ∗Nazwa ): Nazwa(Nazwa) { cout << ”++ Konstruktor obiektu: ” << Nazwa << endl; } ∼ PrzykladKlasy( ) { cout << ”−− Destruktor obiektu: ” << Nazwa << endl; } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . PrzykladKlasy ObA(”A”); int main( ) { cout << ”main: 1” << endl; PrzykladKlasy ObB(”B”); cout << ”main: 2” << endl; { PrzykladKlasy ObC(”C”); } cout << ”main: 3” << endl; } Wynik działania: ++ Konstruktor main: 1 ++ Konstruktor main: 2 ++ Konstruktor −− Destruktor main: 3 −− Destruktor −− Destruktor obiektu: A obiektu: B obiektu: C obiektu: C obiektu: obiektu: B A Utworzenie bloku instrukcji wyznacza czas życia tworzonych statycznie obiektów. c 2005–2010 Bogdan Kreczmer Copyright ° Zakres ważności, przekazywanie obiektów, obiekty tymczasowe 1 Obszar istnienia zmiennych dynamicznych class PrzykladKlasy { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . const char ∗ const Nazwa; public : PrzykladKlasy( const char ∗Nazwa ): Nazwa(Nazwa) { cout << ”++ Konstruktor obiektu: ” << Nazwa << endl; } ∼ PrzykladKlasy( ) { cout << ”−− Destruktor obiektu: ” << Nazwa << endl; } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . int main( ) { cout << ”main: 1” << endl; PrzykladKlasy ∗wOb1; { PrzykladKlasy ∗wOb2 = new PrzykladKlasy(”X”); wOb1 = wOb2; } cout << ”main: 2” << endl; delete wOb1; cout << ”main: 3” << endl; } Wynik działania: main: 1 ++ Konstruktor obiektu: X main: 2 −− Destruktor obiektu: X main: 3 Zmienne tworzone dynamicznie istnieja˛ niezależnie od zakresu ważności deklaracji zmiennej wskaźnikowej. Ich destruktory uruchamiane sa˛ tylko i wyłacznie ˛ w momencie użycia operatora delete. c 2005–2010 Bogdan Kreczmer Copyright ° Zakres ważności, przekazywanie obiektów, obiekty tymczasowe 2 Definiowanie zmiennych w ”locie” class PrzykladKlasy { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . const char ∗ const Nazwa; public : PrzykladKlasy( const char ∗Nazwa ): Nazwa(Nazwa) { cout << ”++ Konstruktor obiektu: ” << Nazwa << endl; } ∼ PrzykladKlasy( ) { cout << ”−− Destruktor obiektu: ” << Nazwa << endl; } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . int main( ) { int Zm = 0; } cout << ”main: 1” << endl; if (Zm == 0) { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . KlasaTest ObA(”A”); } else { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . KlasaTest ObB(”B”); } // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . cout << ”main: 2” << endl; Wynik działania: main: 1 ++ Konstruktor obiektu: A - Destruktor obiektu: A main: 2 Mechanizmy definiowania zmiennych w jezyku ˛ C++ pozwalaja˛ je tworzyć w tych miejscach, w których faktycznie sa˛ potrzebne. c 2005–2010 Bogdan Kreczmer Copyright ° Zakres ważności, przekazywanie obiektów, obiekty tymczasowe 3 Definiowanie zmiennych w ”locie” class PrzykladKlasy { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . const char ∗ const Nazwa; public : PrzykladKlasy( const char ∗Nazwa ): Nazwa(Nazwa) { cout << ”++ Konstruktor obiektu: ” << Nazwa << endl; } ∼ PrzykladKlasy( ) { cout << ”−− Destruktor obiektu: ” << Nazwa << endl; } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . int main( ) { cout << ”main: 1” << endl; } Wynik działania: for ( int licznik = 0; licznik < 2; ++licznik ) { // . . . . . . . . . KlasaTest ObA(”A”); cout << ” Licznik: ” << licznik << endl; } // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . cout << ”main: 2” << endl; hhh ((( h (h (h (h cout << ” Licznik: ” << licznik << hh endl; ((( main: 1 ++ Konstruktor obiektu: A Licznik: 0 - Destruktor obiektu: A ++ Konstruktor obiektu: A Licznik: 1 - Destruktor obiektu: A main: 2 W instrukcji for można definiować i inicjalizować zmienne pełniace ˛ role˛ liczników lub w inny sposób wykorzystywane w trakcie iteracji petli. ˛ Pozwala to utworzyć zmienna˛ dopiero w tym miejscu, w którym jest potrzebna. Deklarowanie zmiennych i obiektów wewnatrz ˛ bloku instrukcji for wiaże ˛ sie˛ z ich kreowaniem i destrukcja˛ przy każdej iteracji petli. ˛ c 2005–2010 Bogdan Kreczmer Copyright ° Zakres ważności, przekazywanie obiektów, obiekty tymczasowe 4 Definiowanie zmiennych w ”locie” if ( int Zm = Funkcja( ) ) { // Zakres widoczności zmiennej Zm } else { // Zakres widoczności zmiennej Zm } switch ( int Zm = Funkcja( ) ) { // Zakres widoczności zmiennej Zm } while ( int Zm = Funkcja( ) ) { // Zakres widoczności zmiennej Zm } W przypadku instrukcji iteracyjnych kreacja i destrukcja zmiennej przebiega tyle razy, ile razy wykonana zostanie dana petla. ˛ c 2005–2010 Bogdan Kreczmer Copyright ° Zakres ważności, przekazywanie obiektów, obiekty tymczasowe 5 Definiowanie zmiennych w ”locie” while ( int Zm = Funkcja( ) ) { { int Zm = Funkcja( ); ... // Zakres widoczności zmiennej Zm } } ( hhh((( hh( h( hhh h( h)h)h != 0 ) { while ( ((int Zm =hFunkcja( (((( (((( { hh ( hh( hh( (( h( h= h( (h( hh ( int((( Zm Funkcja( != hh0; h (((() ) h ... hh ( hh ( ( hh ( // Zakres widoczności zmiennej Zm } } Definiowanie zmiennych w ”locie” nie może łaczyć ˛ ze soba˛ dodatkowych operacji podobnie jak zwykłe definiowanie zmiennych. c 2005–2010 Bogdan Kreczmer Copyright ° Zakres ważności, przekazywanie obiektów, obiekty tymczasowe 6 Przekazywanie parametru przez wartość class KlasaTest { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . const char ∗ const Nazwa; public : void Metoda( KlasaTest Ob ) { cout << ”... Metoda uruchomiona” << endl; } KlasaTest( const char ∗Nazwa ): Nazwa(Nazwa) { cout << ”++ Konstruktor obiektu: ” << Nazwa << endl; } ∼ KlasaTest( ) { cout << ”−− Destruktor obiektu: ” << Nazwa << endl; } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . int main( ) { cout << ”main: 1” << endl; KlasaTest ObA(”A”); cout << ”main: 2” << endl; ObA.Metoda(ObA); cout << ”main: 3” << endl; } Wynik działania: main: 1 ++ Konstruktor obiektu: A main: 2 ... Metoda uruchomiona −− Destruktor obiektu: A main: 3 −− Destruktor obiektu: A Przy przekazywaniu parametru poprzez wartość na poziomie funkcji/metody tworzona jest kopia obiektu. Zakończenie działania funkcji/metody kończy “życie” tej kopii. Ale co z konstruktorem??? c 2005–2010 Bogdan Kreczmer Copyright ° Zakres ważności, przekazywanie obiektów, obiekty tymczasowe 7 Konstruktor kopiujacy ˛ class KlasaTest { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . const char ∗ const Nazwa; public : void Metoda( KlasaTest Ob ) { cout << ”... Metoda uruchomiona” << endl; } KlasaTest( const KlasaTest & Ob ): Nazwa(Ob. Nazwa) { cout << ”++ Konstruktor kopiujacy: ” << Nazwa << endl; } KlasaTest( const char ∗Nazwa ): Nazwa(Nazwa) { cout << ”++ Konstruktor obiektu: ” << Nazwa << endl; } ∼ KlasaTest( ) { cout << ”−− Destruktor obiektu: ” << Nazwa << endl; } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Wynik działania: int main( ) { cout << ”main: 1” << endl; KlasaTest ObA(”A”); cout << ”main: 2” << endl; ObA.Metoda(ObA); cout << ”main: 3” << endl; main: 1 ++ Konstruktor obiektu: A main: 2 ++ Konstruktor kopiujacy: A ... Metoda uruchomiona −− Destruktor obiektu: A main: 3 −− Destruktor obiektu: A } Przy tworzeniu kopii obiektu wywoływany jest niejawnie konstruktor kopiujacy. ˛ W każdej klasie zawsze istnieje taki konstruktor niezależnie od innych konstruktorów. c 2005–2010 Bogdan Kreczmer Copyright ° Zakres ważności, przekazywanie obiektów, obiekty tymczasowe 8 Konstruktor kopiujacy ˛ class KlasaTest { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . const char ∗ const Nazwa; KlasaTest( const KlasaTest & Ob ): Nazwa(Ob. Nazwa) { cout << ”++ Konstruktor kopiujacy: ” << Nazwa << endl; } public : void Metoda( KlasaTest Ob ) { cout << ”... Metoda uruchomiona” << endl; } KlasaTest( const char ∗Nazwa ): Nazwa(Nazwa) { cout << ”++ Konstruktor obiektu: ” << Nazwa << endl; } ∼ KlasaTest( ) { cout << ”−− Destruktor obiektu: ” << Nazwa << endl; } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . int main( ) { KlasaTest ObA(”A”); ( hhh( h( h ((( h; ObA.Metoda(ObA) } Konstruktor kopiujacy ˛ musi być dostepny ˛ przed wywołaniem samej metody. Aby wiec ˛ możliwe było jego wywołanie musi on być dostepny ˛ na poziomie funkcji, z której wywoływana jest dana metoda. c 2005–2010 Bogdan Kreczmer Copyright ° Zakres ważności, przekazywanie obiektów, obiekty tymczasowe 9 Przekazanie parametru przez referencje˛ class KlasaTest { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . const char ∗ const Nazwa; public : void Metoda( KlasaTest & Ob ) { cout << ”... Metoda uruchomiona” << endl; } KlasaTest( const KlasaTest & Ob ): Nazwa(Ob. Nazwa) { cout << ”++ Konstruktor kopiujacy: ” << Nazwa << endl; } KlasaTest( const char ∗Nazwa ): Nazwa(Nazwa) { cout << ”++ Konstruktor obiektu: ” << Nazwa << endl; } ∼ KlasaTest( ) { cout << ”−− Destruktor obiektu: ” << Nazwa << endl; } }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . int main( ) { cout << ”main: 1” << endl; KlasaTest ObA(”A”); cout << ”main: 2” << endl; ObA.Metoda(ObA); cout << ”main: 3” << endl; Wynik działania: main: 1 ++ Konstruktor obiektu: A main: 2 ... Metoda uruchomiona main: 3 −− Destruktor obiektu: A } Przekazywanie parametru poprzez referencje˛ podobnie jak przekazywanie go przez wskaźnik, nie powoduje uruchomienia operacji kopiowania, gdyż metoda/funkcja działa na “oryginalnej” zmiennej. c 2005–2010 Bogdan Kreczmer Copyright ° Zakres ważności, przekazywanie obiektów, obiekty tymczasowe 10 Zwracanie obiektu przez wartość class KlasaTest { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . const char ∗ const Nazwa; public : KlasaTest Metoda( ) { cout << ”... Metoda uruchomiona” << endl; return KlasaTest( ”X” ); } KlasaTest( const KlasaTest & Ob ): Nazwa(Ob. Nazwa) { cout << ”++ Konstruktor kopiujacy: ” << Nazwa << endl; } // . . . . . . . . . . . . . . . . . . . . KlasaTest( const char ∗Nazwa ): Nazwa(Nazwa) { cout << ”++ Konstruktor obiektu: ” << Nazwa << endl; } // . . . . . . . . . . . . . . . . . . . . . . ∼ KlasaTest( ) { cout << ”−− Destruktor obiektu: ” << Nazwa << endl; } // . . . . . . . . . . . . . . . . . . . . . . . . }; //. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Wynik działania: int main( ) { cout << ”main: 1” << endl; KlasaTest ObA(”A”); cout << ”main: 2” << endl; ObA = ObA.Metoda( ); cout << ”main: 3” << endl; main: 1 ++ Konstruktor obiektu: A main: 2 ... Metoda uruchomiona ++ Konstruktor obiektu: X −− Destruktor obiektu: X main: 3 −− Destruktor obiektu: X } Obiekt zwracany ma charakter obiektu tymczasowego, którego istnienie umożliwia przekazanie danych na ”zewnatrz” ˛ funkcji/metody. Po wykonaniu tego zadana jest unicestwiany. c 2005–2010 Bogdan Kreczmer Copyright ° Zakres ważności, przekazywanie obiektów, obiekty tymczasowe 11 Operator przypisania class KlasaTest { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . const char ∗ const Nazwa; public : KlasaTest Metoda( ) { cout << ”... Metoda uruchomiona” << endl; return KlasaTest( ”X” ); } KlasaTest & operator = ( constKlasaTest & Ob ) { cout << ”... Przypisanie: ” << Nazwa << ” <− ” << Ob. Nazwa << endl; return ∗this ; } KlasaTest( const KlasaTest & Ob ): KlasaTest( const char ∗Nazwa ): Nazwa(Ob. Nazwa) { cout << ”++ Konstruktor kopiujacy: ” << Nazwa(Nazwa) { cout << ”++ Konstruktor obiektu: ” << Nazwa << endl; } Nazwa << endl; } ∼ KlasaTest( ) { cout << ”−− Destruktor obiektu: ” << Nazwa << endl; } }; //. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Wynik działania: int main( ) { cout << ”main: 1” << endl; KlasaTest ObA(”A”); cout << ”main: 2” << endl; ObA = ObA.Metoda( ); cout << ”main: 3” << endl; } main: 1 ++ Konstruktor obiektu: A main: 2 ... Metoda uruchomiona ++ Konstruktor obiektu: X ... Przypisanie: A <- X −− Destruktor obiektu: X main: 3 −− Destruktor obiektu: A Implementacja operatora przypisania ograniczajaca ˛ zakres kopiowanych pól umożliwiła w tym przypadku zachowanie “tożsamości” obiektowi, który poddany jest operacji przypisania. c 2005–2010 Bogdan Kreczmer Copyright ° Zakres ważności, przekazywanie obiektów, obiekty tymczasowe 12 Obiekt tymczasowy class KlasaTest { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . const char ∗ const Nazwa; public : KlasaTest Metoda( ) { cout << ”... Metoda uruchomiona” << endl; return KlasaTest( ”X” ); } KlasaTest & operator = ( constKlasaTest & Ob ) { cout << ”... Przypisanie: ” << Nazwa << ” <− ” << Ob. Nazwa << endl; return ∗this ; } KlasaTest( const KlasaTest & Ob ): KlasaTest( const char ∗Nazwa ): Nazwa(Ob. Nazwa) { cout << ”++ Konstruktor kopiujacy: ” << Nazwa(Nazwa) { cout << ”++ Konstruktor obiektu: ” << Nazwa << endl; } Nazwa << endl; } ∼ KlasaTest( ) { cout << ”−− Destruktor obiektu: ” << Nazwa << endl; } }; //. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Wynik działania: int main( ) { cout << ”main: 1” << endl; KlasaTest ObA(”A”); cout << ”main: 2” << endl; ObA = ObA.Metoda( ), cout << ”... cdn.” << endl; cout << ”main: 3” << endl; } main: 1 ++ Konstruktor obiektu: A main: 2 ... Metoda uruchomiona ++ Konstruktor obiektu: X ... Przypisanie: A <- X ... cdn. −− Destruktor obiektu: X main: 3 −− Destruktor obiektu: A Znak ’;’ jest terminatorem wykonywanej operacji. Wyznacza on czas życia obiektów tymczasowych. c 2005–2010 Bogdan Kreczmer Copyright ° Zakres ważności, przekazywanie obiektów, obiekty tymczasowe 13 Zwracanie obiektu przez wartość class KlasaTest { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . const char ∗ const Nazwa; public : KlasaTest Metoda( ) { cout << ”... Metoda uruchomiona” << endl; return ∗this ; } KlasaTest & operator = ( constKlasaTest & Ob ) { cout << ”... Przypisanie: ” << KlasaTest( const KlasaTest & Ob ): KlasaTest( const char ∗Nazwa ): Nazwa << ” <− ” << Ob. Nazwa << endl; return ∗this ; } Nazwa(Ob. Nazwa) { cout << ”++ Konstruktor kopiujacy: ” << Nazwa(Nazwa) { cout << ”++ Konstruktor obiektu: ” << Nazwa << endl; } Nazwa << endl; } ∼ KlasaTest( ) { cout << ”−− Destruktor obiektu: ” << Nazwa << endl; } }; //. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Wynik działania: int main( ) { cout << ”main: 1” << endl; KlasaTest ObA(”A”); cout << ”main: 2” << endl; ObA = ObA.Metoda( ), cout << ”... cdn.” << endl; cout << ”main: 3” << endl; } main: 1 ++ Konstruktor obiektu: A main: 2 ... Metoda uruchomiona ++ Konstruktor kopiujacy: A ... Przypisanie: A <- A ... cdn. −− Destruktor obiektu: A main: 3 −− Destruktor obiektu: A Zwrócenie przez wartość ”samego siebie” powoduje uruchomienie konstruktora kopiujacego. ˛ c 2005–2010 Bogdan Kreczmer Copyright ° Zakres ważności, przekazywanie obiektów, obiekty tymczasowe 14 Zwracanie obiektu przez referencje˛ class KlasaTest { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . const char ∗ const Nazwa; public : KlasaTest & Metoda( ) { cout << ”... Metoda uruchomiona” << endl; return ∗this ; } KlasaTest & operator = ( constKlasaTest & Ob ) { cout << ”... Przypisanie: ” << KlasaTest( const KlasaTest & Ob ): KlasaTest( const char ∗Nazwa ): Nazwa << ” <− ” << Ob. Nazwa << endl; return ∗this ; } Nazwa(Ob. Nazwa) { cout << ”++ Konstruktor kopiujacy: ” << Nazwa(Nazwa) { cout << ”++ Konstruktor obiektu: ” << Nazwa << endl; } Nazwa << endl; } ∼ KlasaTest( ) { cout << ”−− Destruktor obiektu: ” << Nazwa << endl; } }; //. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . int main( ) { cout << ”main: 1” << endl; KlasaTest ObA(”A”); Wynik działania: cout << ”main: 2” << endl; ObA = ObA.Metoda( ), cout << ”... cdn.” << endl; cout << ”main: 3” << endl; } main: 1 ++ Konstruktor obiektu: A main: 2 ... Metoda uruchomiona ... Przypisanie: A <- A ... cdn. main: 3 −− Destruktor obiektu: A Przekazanie obiektu przez referencje˛ nie wymaga wykonania operacji kopiowania. c 2005–2010 Bogdan Kreczmer Copyright ° Zakres ważności, przekazywanie obiektów, obiekty tymczasowe 15 Czas życia obiektów tymczasowych class KlasaTest { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . const char ∗ const Nazwa; public : KlasaTest Metoda(KlasaTest Ob) { cout << ”... Metoda” << endl; return KlasaTest( ”X” ); } KlasaTest & operator = ( constKlasaTest & Ob ) { cout << ”... Przypisanie: ” << KlasaTest( const KlasaTest & Ob ): KlasaTest( const char ∗Nazwa ): Nazwa << ” <− ” << Ob. Nazwa << endl; return ∗this ; } Nazwa(Ob. Nazwa) { cout << ”++ Konstruktor kopiujacy: ” << Nazwa(Nazwa) { cout << ”++ Konstruktor obiektu: ” << Nazwa << endl; } Nazwa << endl; } ∼ KlasaTest( ) { cout << ”−− Destruktor obiektu: ” << Nazwa << endl; } }; //. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Wynik działania: int main( ) { cout << ”main: 1” << endl; KlasaTest ObA(”A”); cout << ”main: 2” << endl; ObA = ObA.Metoda(ObA), cout << ”... cdn.\n”; cout << ”main: 3” << endl; } Zakończenie operacji wyznaczone wystapieniem ˛ znaku ’;’ wyznacza czas życia obiektów tymczasowych. c 2005–2010 Bogdan Kreczmer Copyright ° main: 1 ++ Konstruktor obiektu: A main: 2 ++ Konstruktor kopiujacy: A ... Metoda ++ Konstruktor obiektu: X ... Przypisanie: A <- X ... cdn. −− Destruktor obiektu: X −− Destruktor obiektu: A main: 3 −− Destruktor obiektu: A Zakres ważności, przekazywanie obiektów, obiekty tymczasowe 16 Czas życia obiektów tymczasowych int main( ) { NazWektor W a(”a”, 1, 2), W b(”b”, 1, 1), W c(”c”, 1, 0), W d(”d”, 2, 2), W r(”r”); cout << ”—- Poczatek operacji dodawania —————–” << endl; W r = (W a + W b) + (W c + W d); cout << ”—- Koniec operacji dodawania ——————-” << endl; cout << ”—- Rezultat: ” << W r << endl; } Wykonywaniu poszczególnych operacji towarzyszy tworzenie sie˛ obiektów tymczasowych. Chcac ˛ prześledzić ten proces, należy przeciażyć ˛ odpowiednie konstruktory. Przeciażenia ˛ te musza˛ zapewniać możliwość nazwania obiektów, tak aby można było prześledzić okres życia poszczególnych obiektów. c 2005–2010 Bogdan Kreczmer Copyright ° Zakres ważności, przekazywanie obiektów, obiekty tymczasowe 17 Czas życia obiektów tymczasowych class NazWektor { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . static char ZnakCyfry; public : string Nazwa; float x, y; NazWektor(const char ∗Nazwa, float x = 0, float y = 0 ); NazWektor( const NazWektor & W ); ∼ NazWektor( ); NazWektor operator + ( const NazWektor & W ) const; NazWektor & operator = ( const NazWektor & W ); }; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . char NazWektor:: ZnakCyfry = ’0’; int main( ) { NazWektor W( ”a” , 1, 2 ); } ostream & operator << ( ostream & ostrm, const NazWektor & W ) { return ostrm << W. Nazwa << ”(” << W. x << ”, ” << W. y << ”)”; } cout << W << endl; Wynik działania: a(1, 2) Wprowadzenie dodatkowego pola statycznego pozwala numerować tworzace ˛ sie˛ tymczasowe obiekty. Zaś pole Nazwa pozwala nazywać każdy z obiektów. c 2005–2010 Bogdan Kreczmer Copyright ° Zakres ważności, przekazywanie obiektów, obiekty tymczasowe 18 Definicje konstruktora i destruktora class NazWektor { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . static char ZnakCyfry; public : string Nazwa; float x, y; NazWektor(const char ∗Nazwa, float x = 0, float y = 0 ); ... ∼ NazWektor( ); ... }; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . NazWektor::NazWektor( const char ∗Nazwa, float x, float y ): Nazwa(Nazwa) { x = x; y = y; cout << ”++ Konstruktor: ” << ∗this << endl; int main( ) { } NazWektor W( ”a” , 1, 2 ); } NazWektor::∼ NazWektor( ) { cout << ”++ Destruktor: ” << ∗this << endl; } Wynik działania: ++ Konstruktor: a(1, 2) −− Destruktor: a(1, 2) Wywołanie konstruktora lub destruktora jest sygnalizowane odpowiednim komunikatem na wyjściu standardowym. Podaje on nazwe˛ i wartości pól obiektu. c 2005–2010 Bogdan Kreczmer Copyright ° Zakres ważności, przekazywanie obiektów, obiekty tymczasowe 19 Konstruktor kopiujacy ˛ class NazWektor { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . static char ZnakCyfry; public : string Nazwa; x, y; float ... NazWektor( const NazWektor & W ); ... }; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . NazWektor::NazWektor( const NazWektor & W ): Nazwa(”Tmp”) { Nazwa += ++ ZnakCyfry; x = y = 0; cout << ”.. Kopiuje: ” << ∗this << ” <- ” << W << endl; x = W. x; y = W. y; } int main( ) { NazWektor W( ”a” , 1, 2 ); NazWektor V(W); } Wynik działania: ++ Konstruktor: a(1, 2) ++ Kopiuje: Tmp1(0,0) <- a(1, 2) −− Destruktor: Tmp1(1, 2) −− Destruktor: a(1, 2) Konstruktor kopiujacy ˛ w tym rozwiazaniu ˛ nie powoduje przepisanie nazwy obiektu. Jest to niezbedne, ˛ aby móc zidentyfikować nowy obiekt. Jego nazwa tworzona jest automatycznie. c 2005–2010 Bogdan Kreczmer Copyright ° Zakres ważności, przekazywanie obiektów, obiekty tymczasowe 20 Operator dodawania class NazWektor { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . static char ZnakCyfry; public : string Nazwa; x, y; float ... NazWektor operator + ( const NazWektor& W ) const; ... }; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . NazWektor NazWektor::operator + ( const NazWektor& W ) const { cout << ”.. Dodanie: ” << ∗this << ” + ” << W << endl; string Nazwa( ”Tmp” ); Nazwa += ++ ZnakCyfry; return NazWektor( Nazwa.c str(), x+W. x, y+W. y ); } int main( ) { NazWektor W( ”a” , 1, 2 ); W = W + W; } Wynik działania: ++ Konstruktor: .. Dodanie: a(1, ++ Konstruktor: −− Destruktor: −− Destruktor: a(1, 2) 2) + a(1,2) Tmp1(2, 4) Tmp1(2, 4) a(1, 2) W trakcie realizacji operacji dodawania tworzony jest obiekt tymczasowy, który przekazuje ”na zewnatrz” ˛ wynik działania. c 2005–2010 Bogdan Kreczmer Copyright ° Zakres ważności, przekazywanie obiektów, obiekty tymczasowe 21 Operator przypisania class NazWektor { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . static char ZnakCyfry; public : string Nazwa; x, y; float ... NazWektor & operator = ( const NazWektor & W ); ... }; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . NazWektor & NazWektor::operator = ( const NazWektor & W ) { cout << ”.. Podstawienie: ” << ∗this << ” = ” << W << endl; x = W. x; y = W. y; return ∗this ; } int main( ) { NazWektor W( ”a” , 1, 2 ); W = W; } Wynik działania: ++ Konstruktor: a(1, 2) .. Podstawienie: a(1, 2) = a(1,2) −− Destruktor: a(1, 2) Realizacja operacji podstawienia nie wymaga tworzenia żadnych obiektów tymczasowych. Zarówno argument, jak też wynik działania, zwracane sa˛ przez referencje. c 2005–2010 Bogdan Kreczmer Copyright ° Zakres ważności, przekazywanie obiektów, obiekty tymczasowe 22 W r = (W a + W b) + (W c + W d) class NazWektor { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . static char ZnakCyfry; public : string Nazwa; float x, y; NazWektor(const char ∗Nazwa, float x = 0, float y = 0); NazWektor( const NazWektor & W ); ∼ NazWektor( ); NazWektor operator + ( const NazWektor & W ) const; NazWektor & operator = ( const NazWektor & W ); }; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ... int main() { NazWektor Wa(”a”,1,2), Wb(”b”,1,1), Wc(”c”,1,0), Wd(”d”,2,2); NazWektor Wr(”r”); cout << ”— Poczatek operacji dodawania ——-” << endl; Wr = (Wa + Wb) + (Wc + Wd); cout << ”— Koniec operacji dodawania ———-” << endl; cout << ”— Rezultat: ” << Wr << endl; } c 2005–2010 Bogdan Kreczmer Copyright ° Wynik działania: ++ Konstruktor: ++ Konstruktor: ++ Konstruktor: ++ Konstruktor: ++ Konstruktor: a(1, 2) b(1, 1) c(1, 0) d(2, 2) r(0, 0) — Poczatek operacji dodawania ———.. Dodanie: c(1, 0) + d(2, 2) ++ Konstruktor: Tmp1(3, 2) .. Dodanie: a(1, 2) + b(1, 1) ++ Konstruktor: Tmp2(2, 3) .. Dodanie: Tmp2(2, 3) + Tmp1(3, 2) ++ Konstruktor: Tmp3(5, 5) .. Podstawienie: r(0, 0) = Tmp3(5, 5) ++ Destruktor: Tmp3(5, 5) ++ Destruktor: Tmp2(2, 3) ++ Destruktor: Tmp1(3, 2) — Koniec operacji dodawania ———— — Rezultat: r(5, 5) ++ Destruktor: ++ Destruktor: ++ Destruktor: ++ Destruktor: ++ Destruktor: r(5, 5) d(2, 2) c(1, 0) b(1, 1) a(1, 2) Zakres ważności, przekazywanie obiektów, obiekty tymczasowe 23 W r = (W a + W b) + (W c + W d) ... — Poczatek operacji dodawania ———.. Dodanie: c(1, 0) + d(2, 2) W r = (W a + W b) + (W c + W d); ⇓ ++ Konstruktor: Tmp1(3, 2) .. Dodanie: a(1, 2) + b(1, 1) W r = (W a + W b) + W r = (W a + W b) + Tmp1 Tmp1 ; ; Tmp1 Tmp1 ; ; ⇓ ++ Konstruktor: Tmp2(2, 3) .. Dodanie: Tmp2(2, 3) + Tmp1(3, 2) W r= W r= Tmp2 Tmp2 + + ⇓ ++ Konstruktor: Tmp3(5, 5) .. Podstawienie: r(0, 0) = Tmp3(5, 5) ++ Destruktor: Tmp3(5, 5) ++ Destruktor: Tmp2(2, 3) ++ Destruktor: Tmp1(3, 2) W r= W r= W r Tmp3 Tmp3 ; ; −→; — Koniec operacji dodawania ———— — Rezultat: r(5, 5) ... W trakcie realizacji całej operacji tworza˛ sie˛ obiekty pośrednie, które przekazuja˛ wyniki cz˛eściowe. Wszystkie one kończa˛ swoje istnienie wraz z zakończeniem realizacji danej operacji, tzn. wtedy gdy sterowanie ”przekroczy” znak średnika. c 2005–2010 Bogdan Kreczmer Copyright ° Zakres ważności, przekazywanie obiektów, obiekty tymczasowe 24 Zapobieganie tworzeniu obiektów tymczasowych class Wektor3f { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . public : float x, y; NazWektor3f operator + ( Wektor3f W ) const { return W += ∗this ; } Wektor3f & operator += ( const Wektor3f & W ) { x += W. x; y = W. y; return ∗this ; } }; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Wektor3f W1, W2, W3; W1 = W2 + W3; Ilość obiektów tymczasowych: 2 =⇒ (W1 = W2) += W3; Ilość obiektów tymczasowych: 0 Stosujac ˛ bardziej przemyślany zapis operacji arytmetycznych można całkowicie wyeliminować tworzenie sie˛ obiektów tymczasowych. c 2005–2010 Bogdan Kreczmer Copyright ° Zakres ważności, przekazywanie obiektów, obiekty tymczasowe 25 Podsumowanie Przy stosowaniu przeciaże ˛ ń operatorów zapis złożonych działań zazwyczaj wiaże ˛ sie˛ z powstawaniem obiektów tymczasowych. Ich liczbe˛ można prawie zawsze ograniczyć lub całkowicie je wyeliminować. Powoduje to jednak utrate˛ czytelności zapisu takiego działania. W przypadku gdy wymagania czasowe nie sa˛ krytyczne właściwym może być zachowanie przejrzystości zapisu kosztem efektywności wykonywanych operacji. Takie podejście jest szczególnie pomocne w poczatkowej ˛ fazie tworzenia oprogramowania, gdyż zapobiega powstawaniu przypadkowych błedów ˛ w implementowanych wzorach. c 2005–2010 Bogdan Kreczmer Copyright ° Zakres ważności, przekazywanie obiektów, obiekty tymczasowe 26 Pytania i ćwiczenia 1. Jeżeli byłaby możliwa nastepuj ˛ aca ˛ definicja konstruktora kopiujacego ˛ kopiujacego: ˛ class Klasa { public: Klasa( Klasa Ob ) { } }; to jakie byłyby konsekwencje w momencie jego użycia. 2. Zakładajac, ˛ że klasa Wektor3f jest tak samo zdefiniowana jak w prezentowanym wcześniej przykładzie, należy wyznaczyć liczbe˛ powstajacych ˛ obiektów tymczasowych dla wyrażenia: Wr = Wa + Wb + Wc + Wd; c 2005–2010 Bogdan Kreczmer Copyright ° Zakres ważności, przekazywanie obiektów, obiekty tymczasowe 27