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

Podobne dokumenty