Programowanie obiektowe i C++ dla matematyków

Podobne dokumenty
Programowanie obiektowe i C++ dla matematyków

Programowanie obiektowe i C++ dla matematyków

Programowanie obiektowe i C++ dla matematyków

Programowanie obiektowe i C++ dla matematyków

Programowanie w C++ Wykład 12. Katarzyna Grzelak. 28 maja K.Grzelak (Wykład 12) Programowanie w C++ 1 / 27

Składnia C++ Programowanie Obiektowe Mateusz Cicheński

Programowanie w C++ Wykład 11. Katarzyna Grzelak. 13 maja K.Grzelak (Wykład 11) Programowanie w C++ 1 / 30

Język C++ wykład VI. uzupełnienie notatek: dr Jerzy Białkowski. Programowanie C/C++ Język C++ wykład VI. dr Jarosław Mederski.

Wstęp do Programowania 2

Składnia C++ Programowanie Obiektowe Mateusz Cicheński

Konstruktor kopiujacy

Techniki programowania INP001002Wl rok akademicki 2018/19 semestr letni. Wykład 3. Karol Tarnowski A-1 p.

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy KONSTRUKTORY

public: // interfejs private: // implementacja // składowe klasy protected: // póki nie będziemy dziedziczyć, // to pole nas nie interesuje

Programowanie obiektowe Wykład 3. Dariusz Wardowski. dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 1/21

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

Dzisiejszy wykład. Klasa string. wersja prosta wersja ze zliczaniem odwołań. Wyjątki Specyfikator volatile Semafory

PROE wykład 3 klasa string, przeciążanie funkcji, operatory. dr inż. Jacek Naruniec

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

Wprowadzenie w dziedziczenie. Klasa D dziedziczy klasę B: Klasa B klasa bazowa (base class), klasa D klasa pochodna (derived class).

IMIĘ i NAZWISKO: Pytania i (przykładowe) Odpowiedzi

Programowanie obiektowe

C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów

Programowanie obiektowe w języku C++ dr inż. Jarosław Forenc

Wykład 5: Klasy cz. 3

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy INNE SPOSOBY INICJALIZACJI SKŁADOWYCH OBIEKTU

Operator przypisania. Jest czym innym niż konstruktor kopiujący!

Podstawy Programowania Obiektowego

Wskaźnik może wskazywać na jakąś zmienną, strukturę, tablicę a nawet funkcję. Oto podstawowe operatory niezbędne do operowania wskaźnikami:

Listy powiązane zorientowane obiektowo

Programowanie w języku C++

Język C++ wykład VII. uzupełnienie notatek: dr Jerzy Białkowski. Programowanie C/C++ Język C++ wykład VII. dr Jarosław Mederski. Spis.

2.4 Dziedziczenie. 2.4 Dziedziczenie Przykłady programowania w C - kurs podstawowy

Wskaźniki. nie są konieczne, ale dają językowi siłę i elastyczność są języki w których nie używa się wskaźników typ wskaźnikowy typ pochodny:

Kurs programowania. Wykład 1. Wojciech Macyna. 3 marca 2016

KLASY cz4. Dorota Pylak. destruktory składowe statyczne przeciążanie operatorów. wskaźniki

Zaawansowane programowanie w języku C++ Klasy w C++

Programowanie obiektowe - Przykładowe zadania egzaminacyjne (2005/2006)

Programowanie Obiektowe i C++

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy PRAWA PRZYJACIÓŁ KLASY. Dostęp z zewnątrz: Dostęp z wewnątrz:

Szablony klas, zastosowanie szablonów w programach

Programowanie, część I

Programowanie obiektowe i C++ dla matematyków

Programowanie i struktury danych. Wykład 4 Dr Piotr Cybula

Rozdział 4 KLASY, OBIEKTY, METODY

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy WSKAŹNIKI KLASOWE

Referencje do zmiennych i obiektów

Wstęp do programowania obiektowego. WYKŁAD 3 Dziedziczenie Pola i funkcje statyczne Funkcje zaprzyjaźnione, this

Wykład 4: Klasy i Metody

Lista dwukierunkowa - przykład implementacji destruktorów

Wysokość drzewa Głębokość węzła

11.6 Klasa do obsługi liczb wymiernych

Wstęp do Programowania 2

PARADYGMATY PROGRAMOWANIA Wykład 3

Podstawy programowania skrót z wykładów:

Wykład 1. Program przedmiotu. Programowanie Obiektowe (język C++) Literatura. Program przedmiotu c.d.:

Języki i techniki programowania Ćwiczenia 2

Programowanie 2. Język C++. Wykład 3.

Wyszukiwanie w BST Minimalny i maksymalny klucz. Wyszukiwanie w BST Minimalny klucz. Wyszukiwanie w BST - minimalny klucz Wersja rekurencyjna

JĘZYKI PROGRAMOWANIA Z PROGRAMOWANIEM OBIEKTOWYM. Wykład 6

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy INNE SPOSOBY INICJALIZACJI SKŁADOWYCH OBIEKTU

Informacje ogólne. Karol Trybulec p-programowanie.pl 1. 2 // cialo klasy. class osoba { string imie; string nazwisko; int wiek; int wzrost;

Algorytmy i złożoności. Wykład 3. Listy jednokierunkowe

TEMAT : KLASY DZIEDZICZENIE

Programowanie Obiektowe i C++

Drzewo binarne BST. LABORKA Piotr Ciskowski

Kurs programowania. Wykład 2. Wojciech Macyna. 17 marca 2016

JĘZYKI PROGRAMOWANIA Z PROGRAMOWANIEM OBIEKTOWYM

Programowanie obiektowe

wykład IV uzupełnienie notatek: dr Jerzy Białkowski Programowanie C/C++ Język C, a C++. wykład IV dr Jarosław Mederski Spis Język C++ - wstęp

Programowanie i struktury danych

Drzewo. Drzewo uporządkowane ma ponumerowanych (oznaczonych) następników. Drzewo uporządkowane składa się z węzłów, które zawierają następujące pola:

1 Wskaźniki. 1.1 Główne zastosowania wskaźników

. Podstawy Programowania 2. Drzewa bst - część druga. Arkadiusz Chrobot. 12 maja 2019

Wykład 8: klasy cz. 4

Paradygmaty programowania. Paradygmaty programowania

Klasa jest nowym typem danych zdefiniowanym przez użytkownika. Najprostsza klasa jest po prostu strukturą, np

Wykład V. Programowanie II - semestr II Kierunek Informatyka. dr inż. Janusz Słupik. Wydział Matematyki Stosowanej Politechniki Śląskiej

Wstęp do programowania obiektowego, wykład 7

1. Które składowe klasa posiada zawsze, niezależnie od tego czy je zdefiniujemy, czy nie?

Programowanie - wykład 4

Rzutowanie i konwersje

1. Wartość, jaką odczytuje się z obszaru przydzielonego obiektowi to: a) I - wartość b) definicja obiektu c) typ oboektu d) p - wartość

Podstawy programowania 2. Temat: Drzewa binarne. Przygotował: mgr inż. Tomasz Michno

Wyjątki (exceptions)

Programowanie Obiektowew języku C++ Zadania L4

Języki Programowania. Prowadząca: dr inż. Hanna Zbroszczyk. tel: Konsultacje: piątek:

Wykład 1. Program przedmiotu. Programowanie (język C++) Literatura. Program przedmiotu c.d.:

Tbli Tablice obiektów biktó są tworzone dokładnie d tak samo, jak i tablice, składające się z elementów innego typu

Do czego służą klasy?

C-struktury wykład. Dorota Pylak

Programowanie, część I

Podstawy algorytmiki i programowania - wykład 4 C-struktury

Lab 9 Podstawy Programowania

Podstawy informatyki. Elektrotechnika I rok. Język C++ Operacje na danych - wskaźniki Instrukcja do ćwiczenia

Wykład X. Programowanie. dr inż. Janusz Słupik. Gliwice, Wydział Matematyki Stosowanej Politechniki Śląskiej. c Copyright 2016 Janusz Słupik

Programowanie obiektowe w C++ Wykład 12

Wyliczanie wyrażenia obiekty tymczasowe

Operacje wejścia/wyjścia odsłona pierwsza

Operacje wejścia/wyjścia (odsłona druga) - pliki

Transkrypt:

Programowanie obiektowe i C++ dla matematyków Bartosz Szreder szreder (at) mimuw... 22 XI 2011 Uwaga! Ponieważ już sobie powiedzieliśmy np. o wskaźnikach i referencjach, przez które nie chcemy przegrzebywać wskazywanych obiektów, to od teraz postaram się wstawiać słowo const w sensownych miejscach, celem pokazania pewnych dobrych nawyków. W niedalekiej przyszłości obszar wstawiania tego słówka jeszcze poszerzymy. 1 Kopiowanie obiektów Domyślnie (tzn. o ile nie zdefiniujemy inaczej) przypisywanie do siebie zmiennych złożonych tego samego typu spowoduje zwyczajne przepisanie wartości ich pól składowych. Np. powołując się na przykłady z wcześniejszych zajęć, przypisując do siebie ułamki przekopiujemy ich pola licznik i mianownik: 001 #include <iostream> 002 003 using namespace std; 004 005 struct ulamek { 006 int licznik, mianownik; 007 }; 008 009 int main() 010 { 011 ulamek a, b; 012 013 a.licznik = 5; 014 a.mianownik = 13; 015 b = a; 016 017 cout << "a = " << a.licznik << "/" << a.mianownik << "\n"; 018 cout << "b = " << b.licznik << "/" << b.mianownik << "\n"; 019 return 0; 020 } 1

Czasami jednak takie postępowanie może być przyczyną katastrofalnych błędów. Jeśli bowiem rozważymy inny z naszych przykładów drzewo wyszukiwań binarnych szybko zauważymy, że naiwne kopiowanie wartości pól to nie jest dobry pomysł. Każde BST związane jest z pewnymi obszarami pamięci alokowanymi przez system, do której odwołujemy się przez wskaźnik na korzeń root i oczywiście dalsze wskaźniki potomków korzenia. Jeśli dokonamy przypisania na nową zmienną typu BST, to skopiowana zostanie wartość wskaźnika root oba drzewa korzystać będą z tego samego obszaru pamięci. Zatem jeśli zmodyfikujemy w jakiś sposób jedno z drzew, to te same zmiany będą widoczne także w drugim. Katastrofa nadejdzie w momencie wywoływania destruktorów. Powiedzmy, że mamy dwa drzewa T 1 i T 2 z polem root wskazującym ten sam obszar pamięci oraz przyjmijmy bez straty ogólności, że najpierw destrukcji ulegnie T 1, a w chwilę potem T 2. Oczywiście T 1 zostanie usunięte prawidłowo, bo co mogłoby się zepsuć? Niestety destrukcja T 2 zakończy się naruszeniem ochrony pamięci pole root w tym drzewie nadal wskazuje ten sam adres, w którym jednakże pamięć została już zwolniona. Dwukrotne zwalnianie pamięci pod tym samym adresem jest operacją niedozwoloną. Jeśli definiujemy klasy, których obiekty zawierają wskaźniki do pewnych obszarów pamięci wyłącznych dla tego obiektu, to musimy zadbać o poprawne kopiowanie. Przypisanie a = b zatem musi być operacją, która poprawnie zaalokuje nowe obszary pamięci dla obiektu a i przekopiuje do niej zawartość pamięci obiektu b. Poprawne skopiowanie BST polegałoby na przejściu całego drzewa i utworzeniu kopii każdego jednego węzła. Coś takiego pokazuje poniższy wycinek kodu: 001 #include <iostream> 002 003 using namespace std; 004 005 struct BST { 006 private: 007 struct BST_node { 008 int key; 009 BST_node *left, *right; 010 011 BST_node(int); 012 }; 013 014 BST_node *root; 015 016 const BST_node * BST_search(const BST_node *, int); 017 BST_node * BST_insert(BST_node *, int); 018 void BST_destroy(BST_node *); 019 BST_node * BST_copy(const BST_node *); 020 021 public: 022 bool search(int); 023 void insert(int); 024 025 BST(); 2

026 BST(int); 027 BST(const int *, int); 028 029 BST(); 030 031 void operator=(const BST &); 032 }; 033 034 BST::BST_node::BST_node(int k) 035 { 036 key = k; 037 left = right = NULL; 038 } 039 040 BST::BST_node * BST::BST_copy(const BST_node *node) 041 { 042 if (node == NULL) 043 return NULL; 044 045 BST_node *result = new BST_node(node->key); 046 result->left = BST_copy(node->left); 047 result->right = BST_copy(node->right); 048 return result; 049 } 050 051 void BST::operator=(const BST &tree) 052 { 053 root = BST_copy(tree.root); 054 } 055 056 int main() 057 { 058 BST a, b; 059 060 a.insert(5); 061 a.insert(10); 062 b = a; 063 b.insert(7); 064 065 cout << a.search(7) << "\n"; 066 cout << b.search(7) << "\n"; 067 return 0; 068 } Jako bonus przedstawiony został konstruktor dla BST_node. Aby jeszcze lepiej pokazać moc konstruktorów w upraszczaniu i skracaniu kodu, dodamy konstruktor trójargumentowy, przyjmujący od razu wskaźniki na lewe i prawe 3

poddrzewo: 001 #include <iostream> 002 003 using namespace std; 004 005 struct BST { 006 private: 007 struct BST_node { 008 int key; 009 BST_node *left, *right; 010 011 BST_node(int); 012 BST_node(int, BST_node *, BST_node *); 013 }; 014 015 BST_node *root; 016 017 const BST_node * BST_search(const BST_node *, int); 018 BST_node * BST_insert(BST_node *, int); 019 void BST_destroy(BST_node *); 020 BST_node * BST_copy(const BST_node *); 021 022 public: 023 bool search(int); 024 void insert(int); 025 026 BST(); 027 BST(int); 028 BST(const int *, int); 029 030 BST(); 031 032 void operator=(const BST &); 033 }; 034 035 BST::BST_node::BST_node(int k, 036 BST_node *left_subtree, BST_node *right_subtree) 037 { 038 key = k; 039 left = left_subtree; 040 right = right_subtree; 041 } 042 043 044 BST::BST_node * BST::BST_copy(const BST_node *node) 4

045 { 046 if (node == NULL) 047 return NULL; 048 049 return new BST_node(node->key, 050 BST_copy(node->left), BST_copy(node->right)); 051 } 052 053 void BST::operator=(const BST &tree) 054 { 055 root = BST_copy(tree.root); 056 } 057 058 int main() 059 { 060 BST a, b; 061 062 a.insert(5); 063 a.insert(10); 064 b = a; 065 b.insert(7); 066 067 cout << a.search(7) << "\n"; 068 cout << b.search(7) << "\n"; 069 return 0; 070 } Można to jeszcze uprościć używając tzw. argumentów domyślnych, ale do tego jeszcze wrócimy. Ćwiczenie: przeczytać http://en.wikipedia.org/wiki/rule_of_three_(c++_programming) i zaimplementować konstruktor o sygnaturze BST::BST(const BST &) (konstruktor kopiujący), który poprawnie zbuduje drzewo binarne z drzewa przekazanego w argumencie konstruktora. 1.1 Znowu o pamięci W powyższej implementacji operatora przypisania znajduje się poważny problem, polegający na niesprzątaniu poprzedniego drzewa. Przypisujemy na root adres do nowoprzydzielonego obszaru pamięci niezależnie od tego, czy wcześniej już jakąś pamięć otrzymaliśmy. Poprawne przypisanie dba o takie szczegóły i posprząta po ewentualnym poprzedniku: 001 void BST::operator=(const BST &tree) 002 { 003 destroy(); 004 root = BST_copy(tree.root); 005 } 5

2 Wskaźnik this Pisząc operatory przypisania należy zabezpieczyć się przed sytuacją, w której przypisujemy obiekt na niego samego. Pomijając nawet względy wydajnościowe (przypisanie na siebie zwykle jest mało sensowne i można je po prostu zignorować), takie działanie może prowadzić do pewnych mniej lub bardziej subtelnych błędów. Nietrudno sobie wyobrazić sytuację, w której operacja kopiowania zakłada spójność kopiowanego obiektu w trakcie trwania całej operacji. Tymczasem przypisując na obiekt, z którego kopiujemy zmieniamy ten stan i naruszamy założenie. Aby rozpoznać sytuację kopiowania na siebie możemy użyć specjalnego wskaźnika this. Wskaźnik ten zawiera adres obiektu, w którym aktualnie przebywamy. Możemy używać tego wskaźnika do wywoływania metod i odwoływania się do pól składowych, jednak zwykle jest to nadmiarowe. Poniższe implementacje metody BST::destroy() są równoważne: 001 void BST::destroy() 002 { 003 root = BST_destroy(root); 004 } 005 006 void BST::destroy() 007 { 008 this->root = this->bst_destroy(root); 009 } 010 011 void BST::destroy() 012 { 013 root = BST_destroy(this->root); 014 } 015 016 void BST::destroy() 017 { 018 this->root = this->bst_destroy(this->root); 019 } Jeśli otrzymujemy w operatorze przypisania referencję do obiektu, to porównujemy adres zawarty w this z adresem przekazanego obiektu. Jeśli porównanie wypadnie pomyślnie, to mamy do czynienia z przypisaniem na siebie, które ignorujemy: 001 void BST::operator=(const BST &tree) 002 { 003 if (this == &tree) 004 return; 005 destroy(); 006 root = BST_copy(tree.root); 007 } 6