Wykład 2
1. Przeciążanie funkcji niezależnych, przekazywanie parametrów obiektowych przez wartość, referencję oraz wskaźnik
2. Przeciążanie funkcji składowych, przekazywanie parametrów obiektowych przez wartość, referencję oraz wskaźnik
3. Przeciążanie konstruktora - konstruktor bezparametrowy i z parametrami 4. Konstruktor z parametrami domniemanymi
5. Zwracanie wyniku działania funkcji przez obiekt. Autoreferencja - wskaźnik this
6. Jawny konstruktor kopiowania 7. Inicjowanie obiektów
8. Tworzenie i inicjowanie obiektów posiadających obiekty składowe - konstruktory wieloargumentowe
Zofia Kruczkiewicz, p.325 c3 Programowanie obiektowe w C++, Wykład 21
1. Przeciążanie funkcji niezależnych, przekazywanie parametrów obiektowych przez wartość, referencję oraz wskaźnik
1.1. Przeciążanie funkcji - nadawanie tej samej nazwy w tym samym zasięgu dla różnych operacji (różne ciała funkcji) na różnych typach danych (różne listy parametrów funkcji - liczba i typ parametrów) np.
funkcja wyswietl.
Przy przeciążaniu funkcji wybór egzemplarza funkcji następuje według następujących zasad kolejności konwersji (najlepszego dopasowania):
· Ścisła zgodność: nie trzeba stosować żadnych konwersji (np. nazwa tablicy na wskaźnik elementu tablicy, nazwa funkcji na wskaźnik do funkcji),
· Zgodność przy zastosowaniu promowania w zakresie typów całkowitych (np. char na int, short na int i ich odpowiedniki bez znaku) oraz typów zmiennoprzecinkowych np. float na double
· Zgodność przy zastosowaniu standardowych konwersji (np. int na double, klasa pochodna* na podstawowa*, unsigned int na int)
· Zgodność przy zastosowaniu konwersji zdefiniowanych prze użytkownika
· Zgodność przy zastosowaniu wielokropka .... w deklaracji funkcji Uwagi:
· Przy wywołaniu funkcje z parametrami przekazywanymi przez referencję nie są rozróżniane z parametrami przekazywanymi przez wartość, stąd czasem stosuje się sztuczne zabiegi (np. dodatkowy argument)
np.
void f(int&) i
void f(int) ® void f(int&, int) i void f(int)
· Funkcje, które różnią się jedynie typem zwracanej wartości, nie mogą mieć tej samej nazwy np. int f() i double f()
· Obiekty przekazane jako parametry funkcji niezależnych udostępniają jedynie swoje składowe publiczne (public)
Zofia Kruczkiewicz, p.325 c3 Programowanie obiektowe w C++, Wykład 22
1.2. Przekazywanie parametrów przez:
Sposób Uwagi Przykład
Referencja na stos przekazywany jest niejawnie adres obiektu aktualnego i nie są tworzone
obiekty tymczasowe w
przypadku zgodności typów parametrów aktualnego i formalnego
void wyswietl(punkt &p)
Wskaźnik na stos przekazywany jest jawnie adres obiektu aktualnego i nie są tworzone obiekty automatyczne
void wyswietl(punkt *p)
Wartość na stosie tworzony jest obiekt automatyczny za pomocą konstruktora kopiującego (jawnego lub domyślnego- wykład 1). Po zakończeniu bloku obiektu automatycznego ({}) w funkcji usuwa się obiekt za pomocą destruktora (jawnego lub domyślnego-wykład 1)
void wyswietl(punkt p)
#include <iostream.h>
#include "punkt2_1.h" //plik nagłówkowy i modułowy jak w p. 7 wykład 1
void wyswietl(punkt&); // prototypy niezależnych funkcji przeciążonych wyswietl
void wyswietl(punkt*);
void wyswietl(punkt, char);
void wyswietl(float);
void wyswietl(int);
void main()
{{ punkt p1(1, 2), p2(2, 3);
wyswietl(p1); wyswietl(p2); //p1 i p2 przekazane przez referencje wyswietl(&p1); wyswietl(&p2); //p1 i p2 przekazane przez wskaźnik
wyswietl(p1, 32); wyswietl(p2, 32); // p1 i p2 przekazane przez wartość 32 - spacja,
wyswietl(punkt::liczba_punktow()); //liczba obiektów równa 0 - źle!
wyswietl(p1.odleglosc(p2)); //p1 i p2 przekazane przez referencje
wyswietl(p2.odleglosc(p1));
} //tutaj koniec bloku i zostają usunięte wszystkie obiekty powołane w poprzednim bloku
wyswietl(punkt::liczba_punktow()); } //liczba obiektów równa -2 - źle!
Zofia Kruczkiewicz, p.325 c3 Programowanie obiektowe w C++, Wykład 23
//definicje funkcji przeciążonych
void wyswietl(punkt& p) //przekazywanie przez referencję {/*Na stos zostanie przekazany niejawnie adres aktualnego obiektu p. Wszystkie operacje są wykonywane na obiekcie aktualnym*/
cout<<"Przekazanie przez referencje punktu o ";
cout <<"wspolrzednych: " << p.odcieta() << " "<< p.rzedna() << ’\n’;}
void wyswietl(punkt *p) //przekazywanie przez wskaźnik
{/*Na stos zostanie przekazany jawny adres obiektu aktualnego podstawionego pod p. Wyłuskanie składowych obiektu następuje za pomocą operatora wyboru p® lub (*p). Wszystkie operacje są wykonywane na parametrze aktualnym.*/
cout<<"Przekazanie przez wskaznik punktu o ";
cout <<"wspolrzednych: " << p->odcieta() << " "<< p->rzedna() << '\n';
//lub cout <<"wspolrzednych: "<<(*).podcieta() << " "<<(* p).rzedna() << '\n';}
void wyswietl(punkt p, char) //przekazywanie przez wartość
{/*Zostanie wywołany niejawny konstruktor kopiujący do utworzenia obiektu automatycznego na stosie, kopiujący niestatyczne pola przekazywanego obiektu p, bez inkrementacji pola ile_punktow. Wszystkie operacje są wykonywane na obiekcie automatycznym. W jawnym konstruktorze można odwoływać się do pola statycznego. */
cout<<"Przekazanie przez wartosc punktu o ";
cout <<"wspolrzednych: „ << p.odcieta() << „ „<< p.rzedna();
/*tutaj zostanie wywołany destruktor jawny, zmniejszający o 1 liczbę obiektów pamiętanych w globalnym polu statycznym klasy punkt*/ }
void wyswietl(float w) { cout <<"Odleglosc: " << w << ’\n’;}
void wyswietl(int w) { cout <<"Liczba punktow: " << w << ’\n’;}
Zofia Kruczkiewicz, p.325 c3 Programowanie obiektowe w C++, Wykład 24
2. Przeciążanie funkcji składowych, przekazywanie parametrów obiektowych przez wartość, referencję oraz wskaźnik
Zasady przeciążania funkcji składowych są identyczne z zasadami przeciążania funkcji niezależnych
Obiekty przekazane jako parametry funkcji składowych swojej klasy udostępniają swoje składowe prywatne i zabezpieczone (private i protected), w pozostałych wypadkach jedynie składowe publiczne (public).
#include <iostream.h>
#include "punkt2_2.h"
#include <conio.h>
void wyswietl(punkt&);
void wyswietl(float);
void wyswietl(int);
void main() { clrscr();
{ punkt p1(1, 2), p2(2, 3);
/*przekazywanie przez referencję - Na stos zostanie przekazany niejawnie adres aktualnego obiektu p1 lub p2. Wszystkie operacje są wykonywane na obiekcie aktualnym*/
wyswietl(p1);
wyswietl(p2);
wyswietl(p1.odleglosc(p2));
wyswietl(p2.odleglosc(p1));
wyswietl(punkt::liczba_punktów()); // 2 obiekty
/*przekazywanie przez wskaźnik - Na stos zostanie przekazany jawny adres obiektu &p1 lub
&p2. Wszystkie operacje są wykonywane na parametrze aktualnym.*/
wyswietl(p1.odleglosc(&p2));
wyswietl(p2.odleglosc(&p1));
wyswietl(punkt::liczba_punktów()); // 2 obiekty
/*przekazywanie przez wartość - W metodzie odleglosc zostanie wywołany niejawny konstruktor kopiujący do utworzenia obiektu automatycznego na stosie, kopiujący niestatyczne pola przekazywanego obiektu p1 lub p2, bez inkrementacji pola ile_punktow. */
wyswietl(p2.odleglosc(p1, 2));
wyswietl(p1.odleglosc(p2, 2));
wyswietl(punkt::liczba_punktów()); //0 obiektów
}
wyswietl(punkt::liczba_punktów()); // -2 obiektów
} ... //pozostałe definicje w p.7 wykład 1
Zofia Kruczkiewicz, p.325 c3 Programowanie obiektowe w C++, Wykład 25
#ifndef PUNKT //plik nagłówkowy
#define PUNKT class punkt {
static int ile_punktow;
float x, y;
public:
... //pozostałe deklaracje w klasie w punkt jak w p.1 wykład 2
float odleglosc(punkt&); //deklaracje przeciążonych metod z parametrami obiektowymi
float odleglosc(punkt*);
float odleglosc(punkt, int);
};
#endif
#include "punkt2_2.h" //plik modułowy
#include <iostream.h>
#include <math.h>
... //pozostałe definicje metod jak w p.1 wykład 2
float punkt::odleglosc(punkt& p) //przekazywanie przez referencję
/*Na stos zostanie przekazany niejawnie adres aktualnego obiektu p. Wszystkie operacje są wykonywane na obiekcie aktualnym*/
{ return sqrt(pow(x - p.x, 2) + pow(y - p.y, 2));}
float punkt::odleglosc(punkt* p) //przekazywanie przez wskaźnik
/*Na stos zostanie przekazany jawny adres obiektu aktualnego podstawionego pod p. Wyłuskanie składowych obiektu następuje za pomocą operatora wyboru p® lub (*p).
Wszystkie operacje są wykonywane na parametrze aktualnym.*/
{ return sqrt(pow(x - p->x, 2) + pow(y - p->y, 2));}
float punkt::odleglosc(punkt p, int) //przekazywanie przez wartość
/*Zostanie wywołany niejawny konstruktor kopiujący podczas tworzenia obiektu automatycznego na stosie, kopiujący niestatyczne pola przekazywanego obiektu p, bez inkrementacji pola ile_punktow.
Wszystkie operacje są wykonywane na obiekcie automatycznym. W jawnym konstruktorze można odwoływać się do pola statycznego. */
{ return sqrt(pow(x - p.x, 2) + pow(y - p.y, 2));
/*tutaj zostanie wywołany destruktor jawny, zmniejszający o 1 liczbę obiektów pamiętanych w globalnym polu statycznym klasy punkt*/ }
Zofia Kruczkiewicz, p.325 c3 Programowanie obiektowe w C++, Wykład 26
3. Przeciążanie konstruktora - konstruktor bezparametrowy i z parametrami
· W przypadku braku jawnie zdefiniowanego konstruktora kompilator zawsze dołączy niejawny konstruktor bezparametrowy. W przypadku jawnego zdefiniowanego dowolnego konstruktora kompilator nie dołączy własnej definicji konstruktora bezparametrowego.
· Inicjowanie obiektów z dostarczanymi z zewnątrz wartościami lub bezparametrowo z wartościami z góry ustalonymi dla danej klasy można rozwiązać przez przeciążanie konstruktora lub przez zastosowanie parametrów domniemanych
#include <iostream.h>
#include "punkt2_3.h"
... //pozostałe deklaracje w p.2 wykład 2
void main()
{ { punkt p1(1, 2), p2(2, 3); //wywołanie 2 razy konstruktora z parametrami
wyswietl(p1); // x = 1, y =2 wyswietl(p2); } // x = 2, y =3
{ punkt p1, p2; //wywołanie 2 razy konstruktora bez parametrów
wyswietl(p1); // x = 0, y =0
wyswietl(p2); } // x = 0, y =0
... //pozostałe definicje w p.2 wykład 2
#ifndef PUNKT //plik nagłówkowy
#define PUNKT class punkt
{ static int ile_punktow;
float x, y;
public: punkt (); //konstruktor bez parametrów punkt (float, float); //konstruktor z parametrami
... //deklaracje metod takie jak w p.2 wykład 2
}; #endif
#include "punkt2_3.h" //plik modułowy
#include <iostream.h>
#include <math.h>
... //pozostałe definicje w p.2 wykład 2
punkt::punkt(float xx, float yy)
{ cout<<"Konstruktor z parametrami\n"; x = xx; y = yy; ile_punktow+
+; }
punkt::punkt()
{ cout<<"Konstruktor bez parametrow\n"; x = 0; y = 0; ile_punktow+
+; }
Zofia Kruczkiewicz, p.325 c3 Programowanie obiektowe w C++, Wykład 27
4. Konstruktor z parametrami domniemanymi
Konstruktor z parametrami domniemanymi może zastąpić dwa konstruktory:
jeden z parametrami, drugi bez parametrów przypisujący w ciele funkcji domniemane wartości pól.
#include <iostream.h>
#include "punkt2_4.h"
... //deklaracje jak w p. 2 wykład2
void main()
{ { // wywołanie konstruktora x2 z jawnie podanymi parametrami punkt p1(1, 2), p2(2, 3);
wyswietl(p1); // x = 1, y =2
wyswietl(p2); } // x = 2, y =3
{ //wywołanie konstruktora x2 z domniemanymi wartościami parametrów
// równymi 0
punkt p1, p2;
wyswietl(p1); // x = 0, y =0
wyswietl(p2); } // x = 0, y =0
}
... //definicje jak w p. 2 wykład2
#ifndef PUNKT.H //plik nagłówkowy
#define PUNKT.H class punkt
{ static int ile_punktow;
float x, y;
public:
//deklaracja konstruktora z parametrami domniemanymi
punkt (float = 0.0, float = 0.0);
... //pozostałe deklaracje i wszystkie definicje jak w p.2 wykład 2
#endif
Uwaga: Definicja konstruktora z parametrami domniemanymi o wartości nadanej podczas deklaracji jest identyczna jak konstruktora z parametrami, podaną w p.2 wykład 2.
Zofia Kruczkiewicz, p.325 c3 Programowanie obiektowe w C++, Wykład 28
5. Zwracanie wyniku działania funkcji przez obiekt.
Autoreferencja - wskaźnik this.
Przekazywanie wyniku obiektowego działania funkcji przez:
· referencję do obiektu nielokalnego, przekazanego do funkcji
· wskaźnik do obiektu nielokalnego, przekazanego do funkcji
· wartość - przekazywany jest obiekt utworzony przez konstruktor kopiowania domyślny lub jawnie zadeklarowany i zdefiniowany w postaci Klasa(Klasa&).
Uwagi:
· Funkcja zwracająca wynik działania jako obiekt należący do jej własnej klasy udostępnia prywatne lub zabezpieczone składowe.
· Konstruktory i destruktory nie zwracają wyniku, stąd w deklaracji i definicji na należy stosować wskazania zwracanego typu.
· Wskaźnik this jest przekazywany niejawnie do każdej funkcji składowej za wyjątkiem składowych statycznych. Jest to adres obiektu wywołującego daną metodę.
#include <iostream.h>
#include "punkt2_5.h"
... //deklaracje jak w p. 4 wyklad2
void main()
{ {wyswietl(punkt::liczba_punktów()); //0 punktów
punkt p1(2, 3), p2(1, 2); //2 obiekty
punkt &p3 = p1.skrajny(p2);//p3 może być obiektem referencyjnym z obiektem p1 lub p2
wyswietl(punkt::liczba_punktów()); //2 obiekty
punkt* p4 = p1.skrajny(&p2); //p4 może być wskaźnikiem do obiektu p1 lub p2
wyswietl(punkt::liczba_punktów()); //2 obiekty
punkt p5 = p1.skrajny(p2, 1); //p5 może być kopią obiektu p1 lub p2
/*1. Domyślny konstruktor kopiujący przekazuje p2 do metody skrajny - 3 obiekty 2.Domyślny konstruktor kopiujący przekazuje wynik funkcji przez wartość-4 obiekty 3.destruktor usuwający punkt przekazany przez wartość do metody skrajny*/
wyswietl(punkt::liczba_punktów()); //1 obiekt (a są trzy obiekty - p.
7 )
} //tutaj koniec bloku i zostają usunięte wszystkie obiekty powołane w poprzednim bloku
// czyli 3 razy wywołano destruktor
{ wyswietl(punkt::liczba_punktów()); // -2 obiekty (a jest 0 obiektów) punkt p1, p2;
wyswietl(punkt::liczba_punktów()); // 0 ( a są 2 obiekty)
} // tutaj koniec bloku i zostają usunięte wszystkie obiekty powołane w poprzednim bloku
// czyli 2 razy wywołano destruktor Zofia Kruczkiewicz, p.325 c3 Programowanie obiektowe w C++, Wykład 29
wyswietl(punkt::liczba_punktów()); // -2 (a jest 0 obiektów ) }... //definicje jak w p. 4 wyklad2
Zofia Kruczkiewicz, p.325 c3 Programowanie obiektowe w C++, Wykład 210
#ifndef PUNKT.H //plik nagłówkowy punkt2_5.h
#define PUNKT.H class punkt
{ static int ile_punktow;
float x,y;
public: punkt (float = 0.0, float = 0.0);
~punkt();
void przesun (float, float);
float odleglosc(punkt*);
punkt skrajny(punkt, int); //przez wartość parametr i wynik funkcji
punkt& skrajny(punkt&); //przez referencję parametr i wynik funkcji
punkt* skrajny(punkt*); //przez wskaźnik parametr i wynik funkcji
static int liczba_punktow();
float odcieta();
float rzedna(); };
#endif
#include "punkt2_5.h" //plik modułowy
#include <iostream.h>
... //reszta metod jak w p. 4 wykład 2
//p przekazywany przez wartość i wynik funkcji zwracany przez wartość punkt punkt::skrajny(punkt p, int)
{ /*powstaje obiekt automatyczny jako kopia p za pomocą konstruktora kopiowania*/
if (x >= p.odcieta() || y >=p.rzedna()) return *this;
/*powstaje obiekt za pomocą konstruktora kopiowania zawierający kopię obiektu wywołującego metodę.
Autoreferencja- wskaznik this jest przekazywany przez listę parametrów każdej metody oprócz statycznej*/
return p; //lub tutaj powstaje kopia obiektu p za pomocą konstruktora kopiowania // tutaj usuwany jest obiekt automatyczny i wywołany jest jawny destruktor
}
//p przekazywany przez referencje oraz wynik funkcji zwracany przez referencję punkt& punkt::skrajny(punkt& p)
{ if (x >= p.odcieta() || y >=p.rzedna()) return *this;
return p; }
//p przekazywany przez wskaźnik oraz wynik funkcji zwracany przez wskaźnik punkt* punkt::skrajny(punkt* p)
{ if (x >= p->odcieta() || y >=p->rzedna()) return this;
return p; }
Zofia Kruczkiewicz, p.325 c3 Programowanie obiektowe w C++, Wykład 211
6. Jawny konstruktor kopiowania
Jawny konstruktor kopiowania o postaci Klasa(Klasa&) powinien kopiować do utworzonego obiektu wszystkie pola obiektu przesłanego przez referencję w liście parametrów oraz może wykonywać inne operacje np. aktualizację liczby obiektów przechowywaną w polu statycznym klasy.
Wywołanie konstruktora kopiującego:
· przy przekazywaniu przez wartość obiektów do funkcji/metody
· przy zwracaniu przez wartość wyniku działania funkcji/metody
· przy tworzeniu obiektu wraz z inicjowaniem zawartością innego obiektu należącego do tej samej klasy
#include <iostream.h>
#include "punkt2_6.h"
... // reszta programu jak w p. 5 wykład 2
void wyswietl(punkt *);
void main()
{ {wyswietl(punkt::liczba_punktów()); // 0 obiektów punkt p1(2, 2), p2(1, 5);
wyswietl(punkt::liczba_punktów()); // 2 obiekty
punkt p3 = p1.skrajny(p2, 0); //wynik metody i parametr przez wartość
/*1. Jawny konstruktor kopiujący do przekazania p2 do metody skrajny
2. Jawny konstruktor kopiujący do przekazania wyniku funkcji przez wartość 3. Destruktor usuwający punkt przekazany przez wartość do metody skrajny*/
wyswietl(punkt::liczba_punktów);
// 3 obiekty wyswietl(&p1.suma(p2));
/*1.Jawny konstruktor zwykły dla punktu automatycznego w metodzie suma 2. Jawny konstruktor kopiujący do przekazania przez wartość wyniku funkcji 3.Destruktor usuwający punkt automatyczny przy wyjściu z metody
4.Dstruktor usuwający punkt przekazany przez wartość jako wynik funkcji suma do niezależnej funkcji wyswietl przez wskaźnik */
wyswietl(punkt::liczba_punktów()); // 3 obiekty
punkt p4 = p2; //konstruktor kopiujący tworzy p4 na podstawie p2
wyswietl(punkt::liczba_punktów()); // 4 obiekty
} // tutaj koniec bloku i zostają usunięte wszystkie obiekty powołane w poprzednim bloku // czyli wywołany będzie 4 razy destruktor
{ wyswietl(punkt::liczba_punktów()); // 0 obiektów punkt p1, p2;
wyswietl(punkt::liczba_punktów()); // 2 obiekty
} // tutaj koniec bloku i zostają usunięte wszystkie obiekty powołane w poprzednim bloku // czyli wywołany będzie 2 razy destruktor
wyswietl(punkt::liczba_punktów()); } // 0 obiektów
Zofia Kruczkiewicz, p.325 c3 Programowanie obiektowe w C++, Wykład 212
#ifndef PUNKT.H //plik nagłówkowy punkt2_6.h
#define PUNKT.H class punkt
{
static int ile_punktow;
float x,y;
public: punkt (float = 0.0, float = 0.0);
punkt(punkt&); // jawny konstruktor kopiowania
// użyty w metodach podanych poniżej punkt suma(punkt&); //przez wartość wynik metody
punkt skrajny(punkt, int); //przez wartość parametr i wynik metody
static int info();
void odcieta (float);
void rzedna(float yy) { y = yy;}; //metoda inline - dobre rozwiązanie
... //reszta deklaracji jak w p.5 wykład 2
};
#endif
#include "punkt2_6.h" //plik modułowy
#include <iostream.h>
#include <math.h>
punkt::punkt(float xx, float yy) {
cout<<"Konstruktor zwykly\n";
x = xx; y = yy; ile_punktow++;
}
punkt::punkt(punkt& p) {
cout << "Konstruktor kopiujacy\n";
x = p.odcieta(); y = p.rzedna(); ile_punktow++;
}
punkt punkt::suma(punkt& p) {
punkt pom(x + p.odcieta(),y + p.rzedna());
return pom;
}
void punkt::odcieta(float xx)
{ x = xx; } //taką definicja można jako inline umieścić w bloku deklaracji klasy
... //reszta definicji jak w p.5 wykład 2
Zofia Kruczkiewicz, p.325 c3 Programowanie obiektowe w C++, Wykład 213
7. Inicjowanie obiektów
Przekazywanie parametrów funkcji oraz zwracanie wyniku funkcji z zastosowaniem referencji i wskaźników nie wymaga tworzenia obiektów - muszą jednak istnieć obiekty powiązane referencją lub wskaźnikiem, gdyż bezpośrednio są związane z wynikiem i parametrami funkcji (p. 7.2 - miejsca w przykładowym programie 0, 1, 2, 3, 4, oraz wywołanie wyswietl(punkt&) ).
Przekazywanie przez wartość parametrów i wyniku działania funkcji jest bezpieczne - tworzone są obiekty z wywołaniem konstruktora kopiującego (p. 7.2 - miejsca w programie: 5, 6, 7, 9, 10) zawierające kopie obiektów automatycznych lub statycznych istniejące tak długo, jak będą „potrzebne”
(usuwane z wywołaniem destruktora).
Obiekty tymczasowe są takimi obiektami automatycznymi, które powstają, jeżeli wewnątrz wyrażenia zostanie jawnie wywołany konstruktor zwykły (p. 7.1 - miejsce w programie 1; p. 7.2 - miejsca w programie: 1, 2, 3, 4, 8, 11) i usuwane z wywołaniem destruktora, wtedy gdy jest już niepotrzebny.
Ogólne zasady tworzenia obiektów i inicjowania obiektów:
· obiekt = klasa() (konstruktor zwykły - tworzony jest obiekt tymczasowy, który jest usuwany z wywołaniem destruktora po przekazaniu danych do obiektu po lewej stronie)
· klasa obiekt (..), klasa obiekt, klasa obiekt = klasa() (konstruktor zwykły- tworzony jest obiekt automatyczny lub statyczny)
· klasa obiekt2 = obiekt1 (konstruktor kopiujący - tworzony jest obiekt2 statyczny lub automatyczny zawierający kopie danych z obiekt1)
· klasa obiekt1 = funkcja zwracająca obiekt przez wartość (konstruktor kopiujący - tworzony jest statyczny lub automatyczny obiekt obiekt1 )
· obiekt = funkcja zwracająca obiekt przez wartość (konstruktor kopiujący - tworzony jest obiekt po prawej stronie i następnie usuwany z wywołaniem destruktora tego obiektu po przekazaniu danych do obiektu po lewej stronie)
· obiekt automatyczny-parametr - tworzona jest kopia obiektu przekazanego przez wartość jako parametr funkcji/metody (konstruktor kopiujący)
· obiekt automatyczny-wynik - tworzona jest kopia obiektu przekazanego przez wartość jako wynik działania funkcji/metody (konstruktor kopiujący).
Zofia Kruczkiewicz, p.325 c3 Programowanie obiektowe w C++, Wykład 214
7.1. Tworzenie obiektów z inicjalizatorem
#include <iostream.h>
#include "punkt2_6.h"
... //reszta programu jak w p. 6 wykład 2
void main()
{ { // 0 obiektów
punkt p1(2, 2), p2(1, 5); //konstruktor zwykły tworzy p1 i p2 z jawnymi parametrami
// 2 obiekty
punkt p3; /*konstruktor zwykły z domniemanymi parametrami (konstruktor
bezparametrowy) tworzy p3*/
// 3 obiekty
punkt p4 = 3; //konstruktor zwykły tworzy punkt p4 z parametrami 3 i 0 // 4 obiekty
punkt p5 = p4; //konstruktor kopiujący tworzy p5 na podstawie p4
// 5 obiektów
punkt p6 = punkt(8, 9); //konstruktor zwykły z parametrami jawnymi tworzy obiekt p6;
//równoważne punkt p6(8, 9)
/*tworzony jest obiekt tymczasowy za pomocą konstruktora zwykłego z parametrami i przekazany do p6, następnie obiekt tymczasowy jest usuwany za pomocą destruktora*/
p6 = punkt(3, 6); // 1 - 6 obiektów }
} ... // 0 obiektów
Zofia Kruczkiewicz, p.325 c3 Programowanie obiektowe w C++, Wykład 215
7.2. Tworzenie obiektów związane z wywoływaniem funkcji / metod Zastosowanie funkcji z lewej strony operatora przypisania zwracającej obiektowy wynik działania przez referencje, wskaźnik i wartość (metoda skrajny).
#include <iostream.h>
#include "punkt2_6.h"
... //reszta programu jak w p. 6 wykład 2
void wyswietl(punkt&);
void wyswietl(int);
void main()
{ { punkt p1(2, 2), p2(1, 5);
wyswietl(p1); wyswietl(p2);
//zwracanie obiektowego wyniku metody przez referencje
punkt& p3 = p1.skrajny(p2); //0 //wynik metody i parametr przez referencje
wyswietl(p3); //obiekt p3 jest referencyjny dla obiektu p1
//funkcja zwracająca wynik przez referencje z lewej strony operatora przypisania
/* najpierw jest wywołana metoda skrajny zwracająca referencje do obiektu p1; następnie tworzony jest obiekt tymczasowy o składowych x=8, y=8 z wywołaniem konstruktora zwykłego, przypisanie do p1 i usunięcie obiektu tymczasowego z wywołaniem destruktora*/
p1.skrajny(p2) = punkt(8, 8); //1
wyswietl(p1); //p1.x=8,
p1.y=8
/*najpierw jest wywołana metoda skrajny zwracająca referencje do obiektu p1; następnie tworzony jest obiekt tymczasowy o składowych x=2, y=6 z wywołaniem konstruktora zwykłego, przypisanie do p1 i usunięcie obiektu tymczasowego z wywołaniem destruktora*/
p2.skrajny(p1) = punkt(2, 6); //2
wyswietl(p1); //p1.x=2,
p1.y=6
//zwracanie obiektowego wyniku metody przez wskaźnik
punkt* p4 = p1.skrajny(&p2); //wynik metody i parametr przez wskaźnik
wyswietl(*p4); //wskaźnik p4 zawiera adres obiektu p1 (2, 6)
//funkcja zwracająca wynik przez wskaźnik z lewej strony operatora przypisania
/*metoda skrajny zwraca wskaźnik do obiektu p1; następnie jest tworzony obiekt tymczasowy o składowych x=8, y=8 z wywołaniem konstruktora zwykłego, przypisanie do p1 i usunięcie obiektu tymczasowego z wywołaniem destruktora*/
*p1.skrajny(&p2) = punkt(8, 8); //3
wyswietl(p1); //p1.x=8, p1.y=8
/*metoda skrajny zwraca wskaźnik do obiektu p1; następnie jest tworzony obiekt tymczasowy o składowych x=3, y=7 z wywołaniem konstruktora zwykłego, przypisanie do p1 i usunięcie obiektu Zofia Kruczkiewicz, p.325 c3 Programowanie obiektowe w C++, Wykład 216
tymczasowego z wywołaniem destruktora*/
*p2.skrajny(&p1) = punkt(3, 7); //4
wyswietl(p1); //p1.x=3, p1.y=7
Zofia Kruczkiewicz, p.325 c3 Programowanie obiektowe w C++, Wykład 217
//zwracanie obiektowego wyniku funkcji przez wartość
punkt p5 = p1.skrajny(p2, 0); // 5 //wynik metody i parametr przez wartość
/*1.konstruktor kopiujący do przekazania p2 do metody skrajny 2.konstruktor kopiujący do przekazania wyniku funkcji przez wartość(p1)
3.destruktor usuwający punkt przekazany przez wartość do metody skrajny*/
wyswietl(p5);
// 3 obiekty
//funkcja zwracająca wynik przez wartość z lewej strony operatora przypisania
/* najpierw jest wywołana metoda skrajny przekazująca przez wartość obiekt p2 (utworzenie obiektu automatycznego z wywołaniem konstruktora kopiującego-kopia p2) i zwracająca przez wartość wynik działania (utworzenie obiektu automatycznego z wywołaniem konstruktora kopiującego zawierającego kopie danych obiektu p1 ze względu na wynik działania metody skrajny); następnie tworzony jest obiekt tymczasowy o składowych x=8, y=8 z wywołaniem konstruktora zwykłego, przypisanie do kopii obiektu p1, wyświetlenie wyniku przypisania i usuniecie obiektu tymczasowego z wywołaniem destruktora; na końcu usuwana jest kopia obiektu p1*/
wyswietl(p1.skrajny(p2, 0) = punkt(8, 8)); // 6, 7, 8
wyswietl(p1); //p1.x=3, p1.y=7
/* najpierw jest wywołana metoda skrajny przekazująca przez wartość obiekt p1 (utworzenie obiektu automatycznego z wywołaniem konstruktora kopiującego-kopia p1) i zwracająca przez wartość wynik działania (utworzenie obiektu automatycznego z wywołaniem konstruktora kopiującego zawierającego kopie danych obiektu p1 ze względu na wynik działania metody skrajny); następnie tworzony jest obiekt tymczasowy o składowych x=1, y=1 z wywołaniem konstruktora zwykłego, przypisanie do kopii obiektu p1, wyświetlenie wyniku przypisania i usuniecie obiektu tymczasowego z wywołaniem destruktora; na końcu usuwana jest kopia obiektu p1*/
wyswietl(p2.skrajny(p1, 0) = punkt(1,1)); // 9, 10, 11
wyswietl(p1); //p1.x=3,
p1.y=7
// 3 obiekty
} //tutaj koniec bloku i zostają usunięte wszystkie obiekty powołane w poprzednim bloku
// 0 obiektów }...
Zofia Kruczkiewicz, p.325 c3 Programowanie obiektowe w C++, Wykład 218
8. Tworzenie obiektów posiadających obiekty składowe
Przykład: Zadeklarowano klasę kolo do obsługi kół zawierającą składową obiektową punkt jako współrzędne środka koła oraz promień typu float.
Klasa kolo posiada:
* pole statyczne i metodę statyczną do zliczania liczby kół
* metodę statyczną info zwracającą numer klasy kolo
* metodę obliczającą odległość między środkami koła wywołującego metodę oraz przekazanego przez referencję, metodą odleglosc klasy punkt
* metody zwracające pola: - p_promien typu float oraz p_srodek typu punkt
* konstruktor zwykły z parametrami domniemanymi i listą inicjacyjną (wieloargumentowy) lub bez listy argumentów, liczący obiekty typu kolo
* bezargumentowy lub wieloargumentowy konstruktor kopiujący, liczący obiekty typu kolo
* destruktor aktualizujący między innymi liczbę obiektów typu kolo.
Inicjowanie obiektów zawierających składowe jest podobny do inicjowania obiektów bez składowych obiektowych (wykład 2/p. 7). Kolejność inicjowania obiektów składowych zależy kolejności ich deklaracji w klasie głównej. Do inicjowania obiektów stosuje się konstruktory wieloargumentowe lub bez listy argumentów: zwykłe lub kopiujące.
8.1. Konstruktory bezargumentowe (bez listy argumentów) W przypadku wywołania:
· konstruktora zwykłego kola pierwszy wykonywany jest konstruktor bezparametrowy punktu (wywołanie konstruktora bezparametrowego punktu jako przypadku wywołania konstruktora z parametrami domniemanymi) i następnie ustawiane są jawnie pola punktu za pomocą konstruktora zwykłego koła. Istnieje jednak ograniczenie ze strony hermetyzacji - obiekt składowy punkt nie udostępnia swoich pól danych klasie kolo, dlatego do inicjowania pól obiektu składowego typu punkt zdefiniowano metody przeciążone punkt::odcieta(float) i punkt::rzedna(float).
· konstruktora kopiującego koła zawsze pierwszy jest wykonany zwykły konstruktor bezparametrowy punktu, a potem jawnie kopiowana jest zawartość obiektu przypisywanego do kola, tworzonego za pomocą konstruktora kopiującego kola.
Zofia Kruczkiewicz, p.325 c3 Programowanie obiektowe w C++, Wykład 219
#ifndef KOLO.H
#define KOLO.H
#include "punkt2_6.h"
class kolo
{ static int ile_kol;
float promien;
punkt srodek;
public: kolo (float = 0.0, float = 0.0, float = 0.0);
kolo(kolo&);
~kolo();
float odleglosc(kolo&);
float pole();
static int liczba_kol();
static int info();
float p_promien();
punkt& p_srodek();};
#endif
#include "kolo2_8.h"//plik modułowy z konstruktorami bez listy argumentów
#include <iostream.h>
kolo::kolo(float xx, float yy, float rr)
{ srodek.odcieta(xx); srodek.rzedna(yy); promien = rr; } kolo::kolo(kolo& p)
{ cout<<"Konstruktor kopiujacy kola\n";
srodek = p.p_srodek(); promien = p.p_promien(); ile_kol++; } kolo::~kolo()
{ cout<<"Destruktor kola\n"; ile_kol--;}
float kolo::odleglosc(kolo& p) { return
srodek.odleglosc(&p.p_srodek());}
int kolo::ile_kol = 0;
int kolo::liczba_kol() { return ile_kol;}
int kolo::info() { return 1;}
float kolo::p_promien() { return promien;}
punkt& kolo::p_srodek() { return srodek;}
Uwaga: plik modułowy dla klasy punkt może być taki sam jak w p. 6
Zofia Kruczkiewicz, p.325 c3 Programowanie obiektowe w C++, Wykład 220
wykład 2
Zofia Kruczkiewicz, p.325 c3 Programowanie obiektowe w C++, Wykład 221
8.2. Konstruktory z listą argumentów - konstruktor wieloargumentowy Postać:
Klasa::Nagłówek_konstruktora : lista_inicjacyjna {ciało konstruktora}
Obiekty składowe mogą być zainicjowane za pomocą swoich konstruktorów umieszczonych na liście konstruktora klasy głównej w kolejności deklaracji w klasie, a na końcu konstruktor klasy głównej.
Destruktory wywoływane są w odwrotnej kolejności: najpierw klasy głównej, a potem składowych. W przypadku wywołania:
· zwykłego konstruktora koła: pierwszy wykonywany jest zwykły wieloargumentowy konstruktor punktu z parametrami (kolejność wykonania konstruktorów umieszczonych w liście wynika z kolejności deklaracji w klasie głównej), a następnie konstruktor kola.
· konstruktora kopiującego kola : po wywołaniu pierwszy wykonywany jest wieloargumentowy konstruktor kopiujący punktu (tworzony jest punkt) i potem konstruktor kopiujący kola (tworzone jest kolo). Na liście argumentów konstruktora kopiującego kola można umieścić również zwykły konstruktor klasy punkt.
· w liście inicjowane są również składowe nieobiektowe (składowa promien w liście klasy kolo oraz składowe x, y w klasie punkt).
#include "punkt2_6.h" //plik modułowy klasy punkt z konstruktorami // zawierającymi listę argumentów
...
punkt::punkt(float xx, float yy): x(xx), y(yy) // konstruktor zwykły { ile_punktow++; }
punkt::punkt(punkt& p): x(p.odcieta()), y(p.rzedna()) //konstruktor kopiujący { ile_punktow++; }
...
#include "kolo2_8.h" //plik modułowy klasy kolo z konstruktorami //zawierającymi listę argumentów
...
kolo::kolo(float xx,float yy, float rr) : promien(rr), srodek(xx, yy)
{ cout<<"Konstruktor zwykly kola\n"; ile_kol++; } //konstruktor zwykły
kolo::kolo(kolo& p) : srodek(p.p_srodek()), promien(p.p_promien())
{ cout<<"Konstruktor kopiujacy kola\n"; ile_kol++; } //konstruktor kopiujący ...
Zofia Kruczkiewicz, p.325 c3 Programowanie obiektowe w C++, Wykład 222
Program główny wykorzystujący konstruktory z p. 8.1 i 8.2
Uwaga - należy wykonać dwa programy tworząc dla podanych plików nagłówkowych po dwa różne pliki modułowe (jeden zawierający konstruktory bez listy argumentów i drugi z konstruktorami wieloargumentowymi),
#include <iostream.h>
#include "kolo2_8.h"
#include "punkt2_6.h"
void wyswietl(kolo&);
void wyswietl(punkt&);
void main()
{ {kolo p1(2, 2, 3), p2(1, 5, 6);
wyswietl(kolo::liczba_kol(), kolo::info()); // 2 koła i 2 punkty
wyswietl(punkt::liczba_punktow(), punkt::info());
wyswietl(p1); wyswietl(p2);
kolo p3; // p. 8.1-bezparametrowe konstruktory zwykłe punktu i koła
// p. 8.2-wieloargumentowe bezparametrowe konstruktory zwykłe punktu i koła
wyswietl(p3); // srodek.x=0, srodek.y=0. Promien=0
kolo p4 = 2; // p. 8.1-konstruktory zwykłe: punktu i koła
// p. 8.2-wieloargumentowe konstruktory zwykłe punktu i koła
wyswietl(p4); // srodek.x=2, srodek.y=0, Promien=0 // 4 koła i 4 punkty
kolo p5 = p1; // p.8.1-konstruktor bezparametrowy zwykły punktu i kopiujący koła // p. 8.2-wieloargumentowe konstruktory kopiujące punktu i koła
wyswietl(p5); //srodek.x=2, srodek.y=0. Promien=0 // 5 kół i 5 punktów
kolo p6 = kolo (5, 5, 5); // p. 8.1-konstruktory zwykłe punktu i koła º kolo p6 (5,5,5) // p. 8.2-wieloargumentowe konstruktory zwykłe punktu i koła
wyswietl(p6); // srodek.x=5, srodek.y=5. Promien=5 // 6 kół i 6 punktów
p4 = kolo (6, 6, 6); /* p. 8.1- obiekt kolo tymczasowy za pomocą zwykłych konstruktorów punktu i koła, przypisany do p4 i usunięty za pomocą destruktora*/
/* p. 8.2-obiekt kolo tymczasowy za pomocą wieloargumentowych zwykłych konstruktorów punktu i koła, przypisany do p4 i usunięty za pomocą destruktora kola i punkt*/
wyswietl(p4); // srodek.x=6, srodek.y=6. Promien=6 // 6 kół i 6 punktów
} //wykonanie 6 par destruktorów w kolejności: destruktor koła, i potem punktu
} // 0 kół i 0 punktów
void wyswietl(kolo& p)
{ wyswietl(p.p_srodek()); cout<<"Promien: "<< p.p_promien() << '\n';}
Zofia Kruczkiewicz, p.325 c3 Programowanie obiektowe w C++, Wykład 223