Programowanie Obiektowo Zorientowane w języku c++ Konstruktory Mirosław Głowacki 1 1 Akademia Górniczo-Hutnicza im. Stanisława Staszica w Ktrakowie Wydział Inżynierii Metali i Informatyki Stosowanej Katedra Informatyki Stosowanej i Modelowania Listopad 2016-Październik 2018 Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 1 / 53
Spis treści 1 Cechy konstruktora 2 Konstruowanie obiektów lokalnych 3 Konstruowanie obiektów globalnych 4 Konstrukcja obiektów tworzonych operatorem new 5 Jawne wywołanie konstruktora 6 Dalsze sytuacje, gdy pracuje konstruktor 7 Konstruktor domyślny (domniemany) 8 Lista inicjalizacyjna konstruktora 9 Konstruktory niepubliczne 10 Konstruktor kopiujący (albo inicjalizator kopiujący) 11 Konstruktor konwertujący (konwerter) 12 Wady konstruktorów 13 Destruktor Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 2 / 53
Spis treści 1 Cechy konstruktora 2 Konstruowanie obiektów lokalnych 3 Konstruowanie obiektów globalnych 4 Konstrukcja obiektów tworzonych operatorem new 5 Jawne wywołanie konstruktora 6 Dalsze sytuacje, gdy pracuje konstruktor 7 Konstruktor domyślny (domniemany) 8 Lista inicjalizacyjna konstruktora 9 Konstruktory niepubliczne 10 Konstruktor kopiujący (albo inicjalizator kopiujący) 11 Konstruktor konwertujący (konwerter) 12 Wady konstruktorów 13 Destruktor Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 3 / 53
Cechy konstruktora Konstruktory, to specjalne metody biorące udział w inicjalizacji powstających obiektów klas i posiadające pewne specyficzne cechy: Konstruktor to metoda, która nazywa się tak samo jak klasa. class mojaklasa{ double mojepole; mojaklasa(){ mojepole = 0.; } }; Konstruktor nadaje polom obiektu wartości początkowe. W trakcie definiowania obiektu, przydziela mu się miejsce w pamięci, a dopiero potem pracuje dla niego konstruktor. Należy zwrócić uwagę na fakt, że konstruktor sam nie przydziela obiektowi, a tylko ją inicjalizuje. Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 4 / 53
Cechy konstruktora Najważniejszym aspektem konstruktora jest to, że jeśli klasa ma odpowiedni konstruktor, to jest on automatycznie uruchamiany przy definiowaniu każdego obiektu tej klasy. class mojaklasa{public: double mojepole; mojaklasa(){mojepole = 0.;} mojaklasa(double mp){mojepole = mp;} }; mojaklasa moj1obiekt, moj2obiekt(3.5); mojaklasa moj3obiekt = mojaklasa(); // jawne wywoł. Konstruktor może być i zwykle bywa przeciążany i jest to bardzo częsta praktyka - w definicjach klas widzi się zwykle kilka wersji konstruktora różniących się oczywiście listą argumentów. Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 5 / 53
Cechy konstruktora Konstruktor może inicjalizować obiekty typu const i volatile, ale sam nie może być metodą typu const ani volatile. class Temper{ volatile double tbiez; const double t0; public: Temper() const : t0(0.), tbiez(0.){} Temper() volatile : t0(0.), tbiez(0.){} Temper(): t0(0.), tbiez(0.){}; }; Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 6 / 53
Cechy konstruktora Konstruktor nie może być typu static jego zadaniem jest praca na niestatycznych składnikach klasy. class Temper{... static Temper(double t): t0(t), tbiez(0.)... }; Konstruktor nie może być także metodą typu virtual. class Temper{... virtual Temper(double t): t0(t), tbiez(0.)... }; Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 7 / 53
Cechy konstruktora Nie można posłużyć się adresem konstruktora nie można definiować wskaźników do konstruktorów class punkt{ double x, y; public: punkt(){x = 0.; y = 0.;} double getx(){return x;} }; int main(){ punkt p; double (punkt::*wskfun)(); wskfun = &punkt::getx; cout << (p.*wskfun)(); wskfun = &punkt::punkt; return 0; } Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 8 / 53
Cechy konstruktora Standard C++03 zakłada, że jeśli obiekt ma być składnikiem unii, to jego klasa nie może mieć żadnego konstruktora class punkt{ double x, y; public: punkt()x = 0.; y = 0.; }; class wektor{ double wx, wy; public: wektor()wx = 0.; wy = 0.; }; union unia{ wektor w[1000]; punkt p[1000]; }; Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 9 / 53
Spis treści 1 Cechy konstruktora 2 Konstruowanie obiektów lokalnych 3 Konstruowanie obiektów globalnych 4 Konstrukcja obiektów tworzonych operatorem new 5 Jawne wywołanie konstruktora 6 Dalsze sytuacje, gdy pracuje konstruktor 7 Konstruktor domyślny (domniemany) 8 Lista inicjalizacyjna konstruktora 9 Konstruktory niepubliczne 10 Konstruktor kopiujący (albo inicjalizator kopiujący) 11 Konstruktor konwertujący (konwerter) 12 Wady konstruktorów 13 Destruktor Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 10 / 53
Obiekty lokalne automatyczne Obiekty lokalne automatyczne powstają na stosie w momencie ich definicji w trakcie wykonywania programu. Konstruktor takiego obiektu uruchamiany jest, gdy program napotyka definicję tego obiektu. Przestają one istnieć, gdy sterowanie wychodzi poza blok, w którym zostały powołane do życia. void fun(){ int xloc = 0; xloc++; cout << "Obiekt lokalny: " << xloc << endl; } int main(){ fun(); fun(); return 0; } Obiekt lokalny: 1 Obiekt lokalny: 1 Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 11 / 53
Obiekty lokalne statyczne Obiekty lokalne statyczne definiowane są słowem static. Obiekty takie istnieją od samego początku programu, aż do momentu jego zakończenia, jednak są one dostępne tylko w zakresie ważności bloku, w którym zostały zdefiniowane. Konstrukcja obiektu następuje jeszcze przed rozpoczęciem wykonywania programu procedurą main() void fun(){ static int xlocstat = 0; xlocstat++; cout << "Obiekt statyczny: " << xloc << endl; } int main(){ fun(); fun(); return 0; } Obiekt statyczny: 1 Obiekt statyczny: 2 Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 12 / 53
Spis treści 1 Cechy konstruktora 2 Konstruowanie obiektów lokalnych 3 Konstruowanie obiektów globalnych 4 Konstrukcja obiektów tworzonych operatorem new 5 Jawne wywołanie konstruktora 6 Dalsze sytuacje, gdy pracuje konstruktor 7 Konstruktor domyślny (domniemany) 8 Lista inicjalizacyjna konstruktora 9 Konstruktory niepubliczne 10 Konstruktor kopiujący (albo inicjalizator kopiujący) 11 Konstruktor konwertujący (konwerter) 12 Wady konstruktorów 13 Destruktor Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 13 / 53
Obiekty globalne Obiekty dowolnej klasy zdefiniowane poza wszystkimi funkcjami, są obiektami globalnymi. Zakres ważności obiektu globalnego to plik - użycie takiego obiektu w innych plikach wymaga w tych plikach deklaracji. Czas życia takich obiektów to cały czas wykonywania programu. Konstrukcja obiektu następuje jeszcze przed rozpoczęciem wykonywania programu procedurą main() class punkt{ double x, y; public: punkt(){x = 0.; y = 0.;} }; punkt pglob; int main(){ punkt ploc = pglob; } Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 14 / 53
Spis treści 1 Cechy konstruktora 2 Konstruowanie obiektów lokalnych 3 Konstruowanie obiektów globalnych 4 Konstrukcja obiektów tworzonych operatorem new 5 Jawne wywołanie konstruktora 6 Dalsze sytuacje, gdy pracuje konstruktor 7 Konstruktor domyślny (domniemany) 8 Lista inicjalizacyjna konstruktora 9 Konstruktory niepubliczne 10 Konstruktor kopiujący (albo inicjalizator kopiujący) 11 Konstruktor konwertujący (konwerter) 12 Wady konstruktorów 13 Destruktor Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 15 / 53
Obiekty dynamiczne Obiekt może zostać zdefiniowany dynamicznie operatorem new. class punkt{ public: double x, y; punkt(double x0 = 0., double y0 = 0.){ x = x0; y = y0; } }; ostream& operator<<(ostream& str, punkt& p){ return str << "(" << p.x << ", " << p.y << ")"; } punkt* fun(punkt *pt){ pt = new punkt[100]; for (int i = 0; i < 100; i++) pt[i] = punkt(i, i); return pt; } Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 16 / 53
Obiekty dynamiczne int main(){ punkt *wskp1 = new punkt(1., 2.), *wskp2; wskp2 = fun(wskp2); cout << *wskp1 << " " << wskp2[10] << endl; delete wskp1; delete [] wskp2; return 0; } (1, 2) (10,10) Jest to bardzo pożyteczny sposób alokacji obiektów i ich tablic. Umożliwia utworzenie w trakcie wykonywania programu takiej liczby obiektów, jaka jest wymagana. Obiekty tworzone są na stercie (ang. heap) obszarze pamięci o strukturze drzewa przeznaczonym na dynamiczne struktury danych i dynamiczne obszary pamięci. Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 17 / 53
Obiekty dynamiczne Czas życia obiektu do czasu likwidacji obiektu funkcją delete. Konieczny jest przynajmniej jeden dostępny wskaźnik, któremu przypisany jest wykreowany obiekt inaczej obiekt jest niedostępny. Utrata wskaźnika oznacza brak kontaktu z obiektem - będzie on nadal istniał, ale nie ma już do niego dostępu. Jeśli operator new nie potrafi wykreować obiektu (bo na przykład nie ma już miejsca w pamięci) wówczas konstruktor tego obiektu nie jest uruchamiany. Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 18 / 53
Spis treści 1 Cechy konstruktora 2 Konstruowanie obiektów lokalnych 3 Konstruowanie obiektów globalnych 4 Konstrukcja obiektów tworzonych operatorem new 5 Jawne wywołanie konstruktora 6 Dalsze sytuacje, gdy pracuje konstruktor 7 Konstruktor domyślny (domniemany) 8 Lista inicjalizacyjna konstruktora 9 Konstruktory niepubliczne 10 Konstruktor kopiujący (albo inicjalizator kopiujący) 11 Konstruktor konwertujący (konwerter) 12 Wady konstruktorów 13 Destruktor Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 19 / 53
Jawne wywołanie konstruktora Obiekt może być stworzony przez jawne wywołanie konstruktora. int main(){ punkt tabpunkt[3] = {punkt(1., 2.), punkt(-1., 3.), punkt(7., 2.)}; for (int i =0; i<3; i++) cout << tabpunkt[i]; return 0; } (1, 2)(-1, 3)(7, 2) W efekcie otrzymujemy obiekt, który nie ma nazwy. Czas życia takiego obiektu jest ograniczony do wyrażenia, w którym użyto konstruktora. Należy zwrócić uwagę na sposób użycia konstruktora nie stosuje się notacji: obiekt.metoda(argumenty) Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 20 / 53
Spis treści 1 Cechy konstruktora 2 Konstruowanie obiektów lokalnych 3 Konstruowanie obiektów globalnych 4 Konstrukcja obiektów tworzonych operatorem new 5 Jawne wywołanie konstruktora 6 Dalsze sytuacje, gdy pracuje konstruktor 7 Konstruktor domyślny (domniemany) 8 Lista inicjalizacyjna konstruktora 9 Konstruktory niepubliczne 10 Konstruktor kopiujący (albo inicjalizator kopiujący) 11 Konstruktor konwertujący (konwerter) 12 Wady konstruktorów 13 Destruktor Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 21 / 53
Dalsze sytuacje, gdy pracuje konstruktor Konstruktor jest wywoływany również przy tworzeniu obiektów chwilowych. void fun(punkt p){ cout << p; } int main(){ fun(punkt(-3., 9.)); // parametr chwilowy return 0; } Konstruktor klasy podstawowej wywoływany jest przy kreacji obiektu klasy pochodnej, ale o tym potem. Konstruktor pracuje także jeśli powstaje obiekt, który zawiera w sobie obiekt innej klasy - uruchamiany jest wtedy najpierw konstruktor tego składnika. Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 22 / 53
Konstruktor obiektów zawartych w innej klasie class data{ public: unsigned short d, m, r; data(){cout << "Konstruktor klasy data" << endl;} }; class osoba{ public: string imie, nazwisko; data dur; osoba(string im = "", string nz = "", data du = data()): imie(im), nazwisko(nz), dur(du) {cout << "Konstruktor klasy osoba" << endl;} }; osoba os; Konstruktor klasy data Konstruktor klasy osoba Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 23 / 53
Spis treści 1 Cechy konstruktora 2 Konstruowanie obiektów lokalnych 3 Konstruowanie obiektów globalnych 4 Konstrukcja obiektów tworzonych operatorem new 5 Jawne wywołanie konstruktora 6 Dalsze sytuacje, gdy pracuje konstruktor 7 Konstruktor domyślny (domniemany) 8 Lista inicjalizacyjna konstruktora 9 Konstruktory niepubliczne 10 Konstruktor kopiujący (albo inicjalizator kopiujący) 11 Konstruktor konwertujący (konwerter) 12 Wady konstruktorów 13 Destruktor Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 24 / 53
Konstruktor domyślny Konstruktor domyślny to taki konstruktor, który można wywołać bez żadnego argumentu. class punkt{ double x, y; public: punkt(double x0 = 0., double y0 = 0.) {x = x0, y = y0} // konstruktor domyslny }; Zauważmy, że nie mówimy konstruktor bez argumentów, tylko konstruktor, który można wywołać bez żadnych argumentów. Klasa może mieć tylko jeden konstruktor domniemany. Jeśli klasa nie ma żadnego konstruktora, wówczas sam kompilator wygeneruje dla tej klasy konstruktor domyślny. Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 25 / 53
Konstruktor domyślny, a przeciążanie class punkt{ double x, y; public: punkt(){x = 0.; y = 0.;} // konstruktor domyslny punkt(double x0 = 0., double y0 = 0.) {x = x0; y = y0;} // konstruktor domyslny }; int main(){ punkt p1(1., -3.), p2(-3.); // OK punkt p; // OK punkt tabpunkt[2] = {punkt(1., 2.), punkt(-1., 3.)}; // OK punkt tbpnt[2] = {punkt(1., 2.)}; // OK } W ostatniej linii kodu istnieje konieczność niejawnego użycia konstruktora domyślnego w celu inicjalizacji drugiego elementu tablicy tbpnt Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 26 / 53
Spis treści 1 Cechy konstruktora 2 Konstruowanie obiektów lokalnych 3 Konstruowanie obiektów globalnych 4 Konstrukcja obiektów tworzonych operatorem new 5 Jawne wywołanie konstruktora 6 Dalsze sytuacje, gdy pracuje konstruktor 7 Konstruktor domyślny (domniemany) 8 Lista inicjalizacyjna konstruktora 9 Konstruktory niepubliczne 10 Konstruktor kopiujący (albo inicjalizator kopiujący) 11 Konstruktor konwertujący (konwerter) 12 Wady konstruktorów 13 Destruktor Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 27 / 53
Lista inicjalizacyjna konstruktora Pola można inicjalizować na liście inicjalizacyjnej konstruktora wg. schematu: klasa::klasa(argumenty):lista { cialo_klasy } int fg = 0, fig = 5; class wektor{ static int lw; const int nrw; volatile double czas; double wx, wy; int& nf; public: wektor(): lw(0), nrw(0), czas(0.), wx(0.), nf(fg){ lw = 5; nrw = 0; czas = 10.; // ponowne podstawienie wx = wy = 0.; // ponowne podstawienie nf = fig; } // ponowne podstawienie }; int wektor::lw; // inicjalizacja pola statycznego Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 28 / 53
Lista inicjalizacyjna konstruktora Lista inicjalizacyjna konstruktora specyfikuje jak należy zainicjować niestatyczne pola klasy. Standard C++03 pozwala polom zdefiniowanym przez wartość i nie będącym typu const nadać wartość na dwa sposoby przy użyciu konstruktora : przez listę inicjalizacyjną konstruktora przez zwykłe podstawienie w ciele konstruktora class wektor{ double wx, wy; public: wektor(double x0, double y0): wx(x0) {wy = y0;} }; Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 29 / 53
Lista inicjalizacyjna konstruktora Referencje i pola typu const muszą w standardzie C++03 być inicjalizowane jedynie na liście inicjalizacyjnej konstruktora: int fg = 0, fig = 5; class wektor{ const int nrw; int &nrfig; wektor(): nrw(0.), nrfig(0), nrfig(fg){ nrw = 3.; nrfig = fig;} }; Należy przy tym zwrócić uwagę na następujące fakty: Nadanie wartości początkowej referencji polega na przypisaniu jej do wcześniej istniejącego obiektu - stałe wartości odpadają. W przeciwieństwie do pól typu const referencję można następnie (np. w ciele konstruktora) przypisać innemu obiektowi. Natomiast użycie przypisania: wektor() {nrfig = fig;} bez uprzedniej inicjalizacji jest niemożliwe. Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 30 / 53
Lista inicjalizacyjna konstruktora Inicjalizować można nie tylko argumentem konstruktora, ale również wyrażeniem, w którym można użyć funkcji zwykłej lub nawet metody. class punkt{ double x, y; punkt(double x0 = 0., double y0 = 0.): x(x0), y(y0){} double odl(punkt p); }; class wektor{ double wx, wy, dlug; wektor(punkt p1 = punkt(0., 0.), punkt p2 = punkt(0., 0.)): wx(p2.x - p1.x), wy(p2.y - p1.y), dlug(p1.odl(p2)){} }; Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 31 / 53
Spis treści 1 Cechy konstruktora 2 Konstruowanie obiektów lokalnych 3 Konstruowanie obiektów globalnych 4 Konstrukcja obiektów tworzonych operatorem new 5 Jawne wywołanie konstruktora 6 Dalsze sytuacje, gdy pracuje konstruktor 7 Konstruktor domyślny (domniemany) 8 Lista inicjalizacyjna konstruktora 9 Konstruktory niepubliczne 10 Konstruktor kopiujący (albo inicjalizator kopiujący) 11 Konstruktor konwertujący (konwerter) 12 Wady konstruktorów 13 Destruktor Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 32 / 53
Konstruktory niepubliczne Konstruktor może być niepubliczny. Jest on składnikiem klasy i jako takiego obowiązują go również zwykłe reguły dostępu ustalane za pomocą słów: public:, protected: i private:. Klasa, która nie ma publicznych konstruktorów nazywana jest klasą prywatną. Pomimo tego, iż konstruktor jest niedostępny powszechnie, jest dostępny dla metod tej klasy. Funkcja zaprzyjaźniona czy też klasa zaprzyjaźniona ma również dostęp do prywatnych składników klasy więc może uruchomić prywatny konstruktor. Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 33 / 53
Spis treści 1 Cechy konstruktora 2 Konstruowanie obiektów lokalnych 3 Konstruowanie obiektów globalnych 4 Konstrukcja obiektów tworzonych operatorem new 5 Jawne wywołanie konstruktora 6 Dalsze sytuacje, gdy pracuje konstruktor 7 Konstruktor domyślny (domniemany) 8 Lista inicjalizacyjna konstruktora 9 Konstruktory niepubliczne 10 Konstruktor kopiujący (albo inicjalizator kopiujący) 11 Konstruktor konwertujący (konwerter) 12 Wady konstruktorów 13 Destruktor Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 34 / 53
Konstruktor kopiujący Kopiującym nazywamy konstruktor, który można wywołać z jednym jawnym argumentem będącym referencją typu własnej klasy: klasa::klasa(klasa&) Konstruktor ten tworzy obiekt, który jest kopią innego, już istniejącego obiektu tej klasy. Konstruktor kopiujący nie jest obowiązkowy. Jeśli nie zostanie zdefiniowany wówczas kompilator wygeneruje go sobie sam. Konstruktor kopiujący pracuje w kilku sytuacjach, które można najogólniej podzielić na: przypadki jawne, praca niejawna - bez wiedzy programisty Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 35 / 53
Konstruktor kopiujący Jawne użycie konstruktora kopiującego następuje przy definicji nowego obiektu w następujący sposób: klasa obiekt_wzor; klasa obiekt_nowy = klasa(obiekt_wzor); Niejawne wywołanie konstruktora kopiującego pewnej klasy następuje najczęściej w sytuacjach: jeśli argumentem funkcji jest obiekt tej klasy, a przesyłanie tego obiektu odbywa się przez wartość. gdy funkcja jako swój rezultat zwraca przez wartość obiekt tej klasy. punkt fun(punkt pt){... return pt; } Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 36 / 53
Konstruktor kopiujący Wiemy już, że niemożliwe jest pozbawienie konstruktora kopiującego prawa modyfikowania oryginału. Konstruktor powinien (a nawet musi) odbierać argument jako referencję, ale powinien zagwarantować niezmienność obiektu - konstruktor taki deklaruje się następująco: wektor::wektor(const wektor& wzorzec); Obiekt stanowiącyu parametr aktualny nie musi być koniecznie stały parametrami będą różne obiekty: czasem stałe, czasem nie. Natomiast tak zdeklarowany konstruktor gwarantuje niezmienność przesłanego mu obiektu. Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 37 / 53
Konstruktor kopiujący Jeśli konstruktor kopiujący nie zostanie zdefiniowany, to kompilator postara się go wygenerować automatycznie. Kopiowanie odbywa się wówczas składnik po składniku - powstaje w ten sposób obiekt, który jest idealną kopią wzorca. W takiej sytuacji definiowanie konstruktora nie jest konieczne - do identycznych kopii wystarczy konstruktor kopiujący generowany automatycznie. Są jednak sytuacje, kiedy taka dosłowna kopia jest niepożądana - jest tak wtedy, gdy składnikiem klasy jest wskaźnik. Wtedy koniecznym jest zdefiniowanie swojego własnego konstruktora kopiującego. Rozważmy następującą klasę Osoba : Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 38 / 53
Konstruktor kopiujący class Osoba{ public: string *imie, *nazwisko, *miasto; Osoba(string im = "", string nzw = "", string mst = ""); }; Osoba::Osoba(string im, string nzw, string mst){ imie = new string; nazwisko = new string; miasto = new string; *imie = im; *nazwisko = nzw; *miasto = mst; } Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 39 / 53
Konstruktor kopiujący Spodziewamy się, że wykonanie kodu: int main(){ Osoba jk("jan", "Kowalski", "Warszawa"); Osoba inny_jk = jk; *inny_jk.miasto = "Krakow"; cout << *jk.miasto << ", " << *inny_jk.miasto << endl; return 0; } wygeneruje strumień wyjściowy: Warszawa, Krakow podczas gdy strumień wyjściowy jest następujący: Krakow, Krakow Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 40 / 53
Automatyczny konstruktor kopiujący jk imie nazwisko miasto Jan Kowalski Warszawa Kraków inny_jk imie nazwisko miasto Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 41 / 53
Konstruktor kopiujący Problemem jest wygenerowany automatycznie konstruktor kopiujący. Kopiuje wszystkie pola-wskaźniki klasy, ale nie zmienia przypisanych im adresów. Wszystkie pola-wskaźniki obiektów jk i inny_jk pokazują te same miejsca w pamięci - zmiana zawartości któregokolwiek z tych miejsc dokonywana jest więc dla obu obiektów. Rozwiązaniem jest zdefiniowanie własnego, bardziej inteligentnego konstruktora kopiującego. Osoba::Osoba(const Osoba& os){ imie = new string; nazwisko = new string; miasto = new string; *imie = *os.imie; *nazwisko = *os.nazwisko; *miasto = *miasto; } Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 42 / 53
Własny konstruktor kopiujący jk Jan inny_jk Jan imie nazwisko Kowalski imie nazwisko Kowalski miasto Warszawa miasto Warszawa Kraków Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 43 / 53
Spis treści 1 Cechy konstruktora 2 Konstruowanie obiektów lokalnych 3 Konstruowanie obiektów globalnych 4 Konstrukcja obiektów tworzonych operatorem new 5 Jawne wywołanie konstruktora 6 Dalsze sytuacje, gdy pracuje konstruktor 7 Konstruktor domyślny (domniemany) 8 Lista inicjalizacyjna konstruktora 9 Konstruktory niepubliczne 10 Konstruktor kopiujący (albo inicjalizator kopiujący) 11 Konstruktor konwertujący (konwerter) 12 Wady konstruktorów 13 Destruktor Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 44 / 53
Konstruktor konwertujący Konwertującym nazywamy konstruktor, który można wywołać z jednym jawnym argumentem innej klasy lub typu wbudowanego: class complex{ public: double re, im; complex(double d): re(d), im(0.){} }; ostream& operator<<(ostream& str, const complex& c){ str << "(" << c.re << ", " << c.im << ")"; return str; } int main(){ double mydbl = -3.; complex c(mydbl); cout << "Liczba zespolona c: " << c << endl; } Liczba zespolona c: (-3, 0) Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 45 / 53
Spis treści 1 Cechy konstruktora 2 Konstruowanie obiektów lokalnych 3 Konstruowanie obiektów globalnych 4 Konstrukcja obiektów tworzonych operatorem new 5 Jawne wywołanie konstruktora 6 Dalsze sytuacje, gdy pracuje konstruktor 7 Konstruktor domyślny (domniemany) 8 Lista inicjalizacyjna konstruktora 9 Konstruktory niepubliczne 10 Konstruktor kopiujący (albo inicjalizator kopiujący) 11 Konstruktor konwertujący (konwerter) 12 Wady konstruktorów 13 Destruktor Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 46 / 53
Wady konstruktorów Nie można zdefiniować konstruktora dla typu wbudowanego. Nie można stworzyć konstruktora dla klasy, która nie jest własnością programisty (np.: klasa biblioteczna) - modyfikacje takiej klasy są jednak zwykle niepożądane. Konstruktor konwertujący powinien otrzymywać argumenty typu deklarowanego - w innym przypadku może dochodzić do nieporozumień. Konstruktor konwertujący musi mieć dostęp do składników obcej klasy - tej, której obiekt podlega konwersji i to właśnie ta klasa musi zapewnić mu dostęp poprzez: składniki publiczne, deklarację przyjaźni. co często jest niemożliwe. Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 47 / 53
Spis treści 1 Cechy konstruktora 2 Konstruowanie obiektów lokalnych 3 Konstruowanie obiektów globalnych 4 Konstrukcja obiektów tworzonych operatorem new 5 Jawne wywołanie konstruktora 6 Dalsze sytuacje, gdy pracuje konstruktor 7 Konstruktor domyślny (domniemany) 8 Lista inicjalizacyjna konstruktora 9 Konstruktory niepubliczne 10 Konstruktor kopiujący (albo inicjalizator kopiujący) 11 Konstruktor konwertujący (konwerter) 12 Wady konstruktorów 13 Destruktor Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 48 / 53
Destruktor Destruktorem klasy Klasa jest metoda o nazwie ~Klasa - metoda ta posiada kilka cech charakterystycznych: Destruktor pracuje automatycznie, gdy obiekt jest likwidowany. Klasa nie musi mieć obowiązkowo destruktora, gdyż: nie likwiduje on obiektu, nie zwalnia obszaru pamięci zajmowanego przez obiekt. Rolą destruktora są dodatkowe działania wymagane przed zniszczeniem obiektu - np. gdy konstruktor inkrementuje licznik obiektów, to destruktor powinien zmniejszać ich liczbę. class punkt{ static int liczbapuntow; punkt(){++liczbapuntow;} ~punkt(){liczbapuntow--;} }; int punkt::liczbapuntow; Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 49 / 53
Destruktor Częstym użyciem destruktora jest przypadek dynamicznej rezerwacji pamięci operatorem new - wtedy rolą destruktora jest zwolnienie tej pamięci instrukcją delete. class Osoba{ public: string imie; string nazwisko; string *Dzieci; Osoba(string im = "", string nzw = "", int ld){ imie = im; nazwisko = nzw; Dzieci = new string[ld]} ~Osoba(){delete [] Dzieci} }; Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 50 / 53
Destruktor Destruktor nie może zwracać żadnej wartości - nawet typu void. Destruktor nie posiada żadnych jawnych argumentów nie może być więc przeciążany. Destruktor pracuje automatycznie, gdy obiekt automatyczny lub chwilowy wychodzi ze swojego zakresu ważności. Jeśli obiekt lokalny jest statyczny, to mimo, że kończy się jego zakres ważności, nie jest likwidowany więc także nie pracuje jego destruktor. Likwidacja takiego obiektu następuje dopiero przy zakończeniu programu i wtedy też rusza do pracy destruktor. Jeśli kończy się zakres ważności referencji obiektu destruktor nie jest aktywowany. Analogicznie destruktor nie jest automatycznie wywoływany, gdy wskaźnik do jakiegoś obiektu wychodzi ze swojego zakresu. Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 51 / 53
Destruktor Destruktor może odwołać się do metody swojej klasy. Niemożliwe jest pobranie adresu destruktora. Obiekt klasy mającej destruktor nie może być składnikiem unii. Destruktor nie może być ani const ani volatile, ale może pracować na obiektach swojej klasy typu const i volatile. class Temper{ volatile double tbiez; const double t0; public: Temper(): t0(0.), tbiez(0.){}; ~Temper() const {} ~Temper() volatile {} ~Temper(){} }; const Temper tpieca; volatile Temper twsadu; Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 52 / 53
Destruktor Destruktor można wywołać jawnie poprzez podanie całej jego nazwy: z metod klasy - np. ~klasa(); z innego miejsca w programie - w tym przypadku w przeciwieństwie do jawnego wywołania konstruktora musi być podany obiekt, referencja lub wskaźnik na rzecz którego pracuje - ob.~klasa, ref.~klasa lub wsk->~klasa. Jeśli klasa nie ma destruktora, a mimo to zostanie jawnie wywołany, to wywołanie takie zostanie zignorowane. Można również wywołać destruktor dla typu wbudowanego - takie wywołanie jest dopuszczalne, ale ignorowane. Mirosław Głowacki (AGH, UJK) Programowanie w języku c++ 2016 53 / 53