Kurs języka C++ – wykład 6 ( )

Slides:



Advertisements
Podobne prezentacje
Temat 2: Podstawy programowania Algorytmy – 1 z 2 _________________________________________________________________________________________________________________.
Advertisements

K URS JĘZYKA C++ – WYKŁAD 7 ( ) Konwersje.
1 TREŚĆ UMOWY O PRACĘ : Umowa o pracę określa strony umowy, rodzaj umowy, datę jej zawarcia oraz warunki pracy i płacy, w szczególności: 1) rodzaj pracy,
Blok I: PODSTAWY TECHNIKI Lekcja 7: Charakterystyka pojęć: energia, praca, moc, sprawność, wydajność maszyn (1 godz.) 1. Energia mechaniczna 2. Praca 3.
Tworzenie odwołania zewnętrznego (łącza) do zakresu komórek w innym skoroszycie Możliwości efektywnego stosowania odwołań zewnętrznych Odwołania zewnętrzne.
Wyszukiwanie informacji w Internecie. Czym jest wyszukiwarka? INTERNET ZASOBY ZAINDEKSOWANE PRZEZ WYSZUKIWARKI Wyszukiwarka to mechanizm, który za pomocą.
Excel 2007 dla średniozaawansowanych zajęcia z dnia
Poczta elektroniczna – e- mail Gmail zakładanie konta. Wysyłanie wiadomości.
EWALUACJA JAKO ISTOTNY ELEMENT PROJEKTÓW SYSTEMOWYCH Sonia Rzeczkowska.
… przemy ś lenia pedagogiczne. „Najważniejszym okresem w życiu nie są lata studiowania na wyższej uczelni, ale te najwcześniejsze, czyli okres od narodzenia.
Algorytmy Informatyka Zakres rozszerzony
Model warstwowy OSI Model OSI (Open Systems Interconnection) opisuje sposób przepływu informacji między aplikacjami programowymi w jednej stacji sieciowej.
Instalacja nienadzorowana windows xp Jakub klafta.
Python. Języki Programistyczne Microcode Machine code Assembly Language (symboliczna reprezentacja machine code) Low-level Programming Language (FORTRAN,
Optymalna wielkość produkcji przedsiębiorstwa działającego w doskonałej konkurencji (analiza krótkookresowa) Przypomnijmy założenia modelu doskonałej.
Definiowanie i planowanie zadań typu P 1.  Planowanie zadań typu P  Zadania typu P to zadania unikalne służące zwykle dokonaniu jednorazowej, konkretnej.
1 Definiowanie i planowanie zadań budżetowych typu B.
Jak tworzymy katalog alfabetyczny? Oprac.Regina Lewańska.
API jądra do obsługi przestrzeni użytkownika Co jest tematem tej prezentacji: Transport danych pomiędzy przestrzeniami użytkownika i jądra (asm/uaccess.h)
Język Java Paweł Rajba
Dziedziczenie, polimorfizm, Interfejsy
Test analizy wariancji dla wielu średnich – klasyfikacja pojedyncza
Programowanie Obiektowe – Wykład 1
Lokalne źródła prawa – zarys
Komunikacja ze szkołą i nauczycielami - dziennik elektroniczny
Schematy blokowe.
Kurs języka C++ – wykład 2 ( )
DEFINICJA I ZASTOSOWANIE W JĘZYKU HASKELL
On-the-Fly Garbage Collection
Liczby pierwsze.
Akademia C# - Lab2 Zmienne, instrukcje warunkowe, pętle, debugger,
Rekursje Tak jak w innych językach funkcje mogą odwoływać się same do siebie Możemy regulować głębokość przed stwierdzeniem błędu (MaxRecursion, $RecursionLimit,
Akademia C# lab. 9 Zdarzenia i delegaty.
Programowanie obiektowe Wykład 10
Programowanie obiektowe
Wstęp do Informatyki - Wykład 3
Optymalizacja programów Open-Source
Lekcja 1 – Hello World! Visual Studio, coś łatwego na początek 
Programowanie obiektowe
Wstęp do Informatyki - Wykład 9
Kurs języka C++ – wykład 13 ( )
Elementy fizyki kwantowej i budowy materii
PROGRAMY DO KONTROLI RODZICIELSKIEJ
Programowanie obiektowe
Git - system kontroli wersji
Bezpieczeństwo dostępu do danych w systemie Windows
Języki programowania.
Programowanie obiektowe
Tensor naprężeń Cauchyego
Instrukcje wyboru.
Damian Urbańczyk xHTML Tworzenie stylów CSS.
Koszyk danych.
Podstawy informatyki Zygfryd Głowacz.
ALGORYTMY I STRUKTURY DANYCH
Strukturalne wzorce projektowe
Zmienne i typy danych w C#
Proste obliczenia w arkuszu kalkulacyjnym
FORMUŁOWANIE HIPOTEZ STATYSTYCZNYCH
Implementacja rekurencji w języku Haskell
Znajdowanie liczb pierwszych w zbiorze
POZNAJEMY PULPIT Opracowanie: mgr Barbara Benisz SP nr 20 w Rybniku
Język C++ Preprocesor Łukasz Sztangret Katedra Informatyki Stosowanej i Modelowania Prezentacja przygotowana w oparciu o materiały Danuty Szeligi i Pawła.
Język C++ Operatory Łukasz Sztangret Katedra Informatyki Stosowanej i Modelowania Prezentacja przygotowana w oparciu o materiały Danuty Szeligi i Pawła.
Kwerendy funkcjonalne (Action queries)
Wyrok WSA w Bydgoszczy z dnia 27 października 2016 r., I SA/Bd 613/16
Program na dziś Wprowadzenie Logika prezentacji i artykułu
Autor: Magdalena Linowiecka
Projektowanie systemów informatycznych Wykład 3 – Obiekt a klasa
Zapis prezentacji:

Kurs języka C++ – wykład 6 (4.04.2017) Polimorfizm

Spis treści Metody wirtualne Implementacja polimorfizmu Wczesne i późne wiązanie metod wirtualnych Klasy abstrakcyjne Klasa pair<> Klasa vector<>

Składowe funkcje wirtualne Składowe funkcje wirtualne pozwalają na przedefiniowanie w każdej klasie pochodnej funkcji składowych zadeklarowanych w klasie bazowej. Poprzez funkcje wirtualne w programie zapewnione jest wywołanie metody najlepiej odpowiadającej obiektowi. Składowe funkcje wirtualne należy opatrzyć deklaratorem virtual (wewnątrz klasy). Składowe funkcje wirtualne nadpisywane w klasach pochodnych należy opatrzyć deklaratorem override (wewnątrz klasy). W definicji metody wirtualnej lub wirtualnej i nadpisywanej poza klasą nie używa się ani deklaratora virtual ani deklaratora override.

Składowe funkcje wirtualne Przykład deklaracji klas z metodami wirtualnymi i zwykłymi: class bazowa { public: void opis_zwykly (); virtual void opis_wirtualny (); }; class pochodna: public bazowa void opis_wirtualny () override;

Składowe funkcje wirtualne Przykład definicji metod wirtualnych i zwykłych: void bazowa::opis_zwykly() { cout << "bazowa::opis_zwykly()" << endl; } void bazowa::opis_wirtualny() { cout << "bazowa::opis_wirtualny()" << endl; } void pochodna::opis_zwykly() { cout << "pochodna::opis_zwykly()" << endl; } void pochodna::opis_wirtualny() { cout << "pochodna::opis_wirtualny()" << endl; }

Składowe funkcje wirtualne Przykład użycia metod wirtualnych i zwykłych: bazowa *a = new bazowa(); a->opis_zwykly(); a->opis_wirtualny(); // bazowa::opis_zwykly() // bazowa::opis_wirtualny() bazowa *b = new pochodna(); b->opis_zwykly(); b->opis_wirtualny(); // pochodna::opis_wirtualny()

Składowe funkcje wirtualne Funkcja wirtualna musi być zdefiniowana dla klasy, w której po raz pierwszy została zadeklarowana. Funkcji wirtualnej można używać nawet wtedy, gdy z jej klasy nie wyprowadzi się żadnej klasy pochodnej. Klasa pochodna, która nie potrzebuje specjalnej wersji funkcji wirtualnej, nie musi jej dostarczać. Funkcja w klasie pochodnej z tą samą nazwą i z tą samą listą argumentów co funkcja wirtualna w klasie podstawowej nadpisuje (ang. override) starą wersję funkcji wirtualnej z klasy bazowej.

Polimorfizm Uzyskanie zachowania się funkcji adekwatnego do typu obiektu nazywa się polimorfizmem (ang. polymorphism). Klasa z funkcjami wirtualnymi nazywa się klasą polimorficzną. Aby zachowanie obiektu było polimorficzne należy się do niego odnosić za pomocą wskaźnika albo referencji. Dzięki polimorfizmowi programy stają się rozszerzalne (ang. extensibility) – modyfikacja kodu polega na dodaniu nowej klasy bez potrzeby zmian w kodzie istniejącym.

Implementacja zachowań polimorficznych Obiekty klas polimorficznych mają dodatkowe pole identyfikujące typ obiektu. Decyzję o wyborze funkcji do wykonania podejmuje się w trakcie działania programu (jest to tak zwane późne wiązanie, w przeciwieństwie do zwykłych funkcji gdzie obowiązuje wczesne wiązanie). Każda klasa polimorficzna posiada swoje miejsce w tablicy metod wirtualnych. Polimorfizm jest więc kosztowny (miejsce i czas) – dlatego nie wszystkie metody są wirtualne.

Rezultat funkcji wirtualnej Przy nadpisywaniu funkcji wirtualnej trzeba zachować odpowiedni typ rezultatu: albo rezultat musi być identyczny, albo rezultat musi być kowariantny (referencja lub wskaźnik do obiektu tej samej klasy lub do klasy, dla której jest ona jednoznaczną i dostępną klasą bazową). Przykład: owoc * bazowa::fun () {/*…*/} pomelo * pochodna::fun () {/*…*/}

Inne cechy funkcji wirtualnych Funkcja wirtualna w klasie nie może być statyczna. Funkcja wirtualna w klasie nie może być wbudowana. Dostęp do funkcji wirtualnej może być zmieniony w klasach pochodnych (co zależy od sposobu dziedziczenia) – dostęp ten zależy więc tylko od typu wskaźnika albo referencji. Funkcje wirtualne mogą być przyjacielami w innych klasach.

Funkcja wirtualna wcześnie związana Funkcja wirtualna będzie wcześnie związana gdy będzie wywołana na rzecz konkretnego obiektu znanego z nazwy: klasa ob; // … ob.funwirt(); Funkcja wirtualna będzie wcześnie związana gdy użyjemy kwalifikatora zakresu: wsk->klasa::funwirt(); ref.klasa::funwirt(); Funkcja wirtualna będzie wcześnie związana gdy wywołamy ją w konstruktorze. Funkcja wirtualna może być wbudowana.

Klasy abstrakcyjne Klasy abstrakcyjne służą do definiowania interfejsów (pojęć abstrakcyjnych). Klasa abstrakcyjna zawiera co najmniej jedną abstrakcyjną metodą wirtualną (funkcja czysto wirtualna). Deklaracja metody czysto wirtualnej wygląda następująco: virtual typ funkcja (lista-argumentów) = 0; Nie trzeba (ale można) podawać definicji metody czysto wirtualnej. W klasach potomnych, które nie mają być klasami abstrakcyjnymi, należy zdefiniować wszystkie odziedziczone metody abstrakcyjne. Klasa potomna, w której nie będą zdefiniowane wszystkie odziedziczone metody abstrakcyjne, będzie nadal klasą abstrakcyjną.

Klasy abstrakcyjne Nie wszystkie metody w klasie abstrakcyjnej muszą być abstrakcyjne. Żaden konstruktor ani destruktor nie może być abstrakcyjny. Nie można utworzyć obiektu klasy abstrakcyjnej: nie wolno zdefiniować funkcji, która odbierałaby argument takiej klasy przez wartość; nie wolno zdefiniować funkcji, która zwracałaby wynik takiej klasy przez wartość; klasa abstrakcyjna nie może być typem w jawnej konwersji.

Wirtualny destruktor W klasach polimorficznych (zawierających metody wirtualne) destruktor definiujemy jako wirtualny.

Konstruktor nie może być wirtualny ale… Czasami istnieje potrzeba wyprodukowania nowego obiektu tej samej klasy – w takiej sytuacji można zdefiniować funkcję wirtualną, która będzie przygotowywać taki obiekt (zastąpi konstruktor domyślny albo kopiujący).

Kurs języka C++ – wykład 6 (4.04.2017) Przestrzenie nazw

Spis treści Czym jest przestrzeń nazw Definicja przestrzeni nazw Deklaracja użycia Dyrektywa użycia Anonimowe przestrzenie nazw Poszukiwanie nazw w przestrzeniach Aliasy przestrzeni nazw Komponowanie i wybór w kontekście tworzenia nowej przestrzeni nazw Przestrzenie nazw są otwarte Przestrzeń nazw std

Czym jest przestrzeń nazw Przestrzeń nazw to obszar, w którym umieszcza się różne deklaracje i definicje. Przestrzeń nazw definiuje zasięg, w którym dane nazwy będą obowiązywać i będą dostępne. Przestrzenie nazw rozwiązują problem kolizji nazw. Przestrzenie nazw wspierają modularność kodu.

Definicja przestrzeni nazw Przestrzeń nazw tworzymy za pomocą słowa kluczowego namespace, ograniczając zawartość klamrami: namespace przestrzeń { // deklaracje i definicje } Aby odnieść się do typu, funkcji albo obiektu umieszczonego w przestrzeni nazw musimy stosować kwalifikator zakresu przestrzeń:: poza tą przestrzenią. Funkcja main() musi być globalna, aby środowisko uruchomieniowe rozpoznało ją jako funkcję specjalną. Do nazw globalnych odnosimy się za pomocą pustego kwalifikatora zakresu ::, na przykład ::wspolczynnik. Jeśli w przestrzeni nazw zdefiniujemy klasę to do składowej statycznej w takiej klasie odnosimy się kwalifikując najpierw nazwą przestrzeni a potem nazwą klasy przestrzeń::klasa::składowa.

Definicja przestrzeni nazw Przykład przestrzeni nazw: namespace wybory { int min2 (int, int); int min3 (int, int, int); } int wybory::min2 (int a, int b) { return a<b ? a : b; } int wybory::min3 (int a , int b , int c) { return min2(min2(a,b),c); } int min4 (int a, int, b, int c, int d) { return wybory::min2( wybory::min2(a,b), wybory::min2(c,d)); }

Deklaracja użycia Deklaracja użycia wprowadza lokalny synonim nazwy z innej przestrzeni nazw (wskazanej nazwy można wówczas używać bez kwalifikowania jej nazwą przestrzeni). Deklaracja użycia using ma postać: using przestrzeń::symbol; Deklaracja użycia obowiązuje do końca bloku, w którym wystąpiła. Deklaracje użycia stosujemy w celu poprawienia czytelności kodu. Deklaracje użycia należy stosować tak lokalnie, jak to jest możliwe. Jeśli większość funkcji w danej przestrzeni nazw korzysta z jakiejś nazwy z innej przestrzeni, to deklaracje użycia można włączyć do przestrzeni nazw.

Dyrektywa użycia Dyrektywa użycia udostępnia wszystkie nazwy z określonej przestrzeni nazw. Dyrektywa użycia using namespace ma postać: using namespace przestrzeń; Dyrektywy użycia stosuje się najczęściej w funkcjach, w których korzysta się z wielu symboli z innej niż ta funkcja przestrzeni nazw. Globalne dyrektywy użycia są stosowane do transformacji kodu i nie powinno się ich stosować do innych celów. Globalne dyrektywy użycia w pojedynczych jednostkach translacji (w plikach .cpp) są dopuszczalne w programach testowych czy w przykładach, ale w produkcyjnym kodzie jest to niestosowne i jest uważane za błąd. Globalnych dyrektyw użycia nie wolno stosować w plikach nagłówkowych!

Anonimowe przestrzenie nazw Anonimową przestrzeń nazw tworzymy za pomocą słowa kluczowego namespace bez nazwy, ograniczając zawartość klamrami: namespace { // deklaracje i definicje } Anonimowa przestrzeń nazw zastępuje użycie deklaratora static przy nazwie globalnej – dostęp do nazw zdefiniowanych w przestrzeni anonimowej jest ograniczony do bieżącego pliku. Dostęp do anonimowej przestrzeni nazw jest możliwy dzięki niejawnej dyrektywie użycia. namespace $$$ { // deklaracje i definicje } using namespace $$$; W anonimowej przestrzeni nazw $$$ jest unikatową nazwą w zasięgu, w którym jest zdefiniowana ta przestrzeń.

Poszukiwanie nazw w przestrzeniach Gdy definiujemy funkcję z jakiejś przestrzeni nazw (przed nazwą definiowanej właśnie funkcji stoi kwalifikator przestrzeni) to w jej wnętrzu dostępne są wszystkie nazwy z tej przestrzeni. Funkcja z argumentem typu T jest najczęściej zdefiniowana w tej samej przestrzeni nazw co T. Jeżeli więc nie można znaleźć funkcji w kontekście, w którym się jej używa, to szuka się jej w przestrzeniach nazw jej argumentów. Jeżeli funkcję wywołuje metoda klasy K, to pierwszeństwo przed funkcjami znalezionymi przez typy argumentów mają metody z klasy K i jej klas bazowych.

Aliasy przestrzeni nazw Jeżeli użytkownicy nadają przestrzeniom nazw krótkie nazwy, to mogą one spowodować konflikt. Długie nazwy są niewygodne w użyciu. Dylemat ten można rozwiązać za pomocą krótkiego aliasu dla długiej nazwy przestrzeni nazw. Aliasy dla przestrzeni nazw tworzymy za pomocą słowa kluczowego namespace z dwiema nazwami namespace krótka = długa_nazwa_przestrzeni; Przykład: namespace American_Telephone_and_Telegraph { // tutaj zdefiniowano Napis } namespace ATT = American_Telephone_and_Telegraph; American_Telephone_and_Telegraph::Napis n = "x"; ATT::Napis nn = "y"; Nadużywanie aliasów może prowadzić do nieporozumień!

Komponowanie i wybór Interfejsy projektuje się po to, by zminimalizować zależności pomiędzy różnymi częściami programu. Minimalne interfejsy prowadzą do systemów łatwiejszych do zrozumienia, w których lepiej ukrywa się dane i implementację, łatwiej się je modyfikuje oraz szybciej kompiluje. Eleganckim narzędziem do konstruowania interfejsów są przestrzenie nazw.

Komponowanie i wybór Gdy chcemy utworzyć interfejs z istniejących już interfejsów to stosujemy komponowanie przestrzeni nazw za pomocą dyrektyw użycia, na przykład: namespace His_string { class String { /* ... */ }; String operator+ (const String&, const String&); String operator+ (const String&, const char*); void fill (char) ; // ... } namespace Her_vector { template<class T> class Vector { /* ... */ }; // ... } namespace My_lib { using namespace His_string; using namespace Her_vector; void my_fct(String&) ; } Dyrektywa użycia wprowadza do zasięgu wszystkie deklarację z podanej przestrzeni nazw.

Komponowanie i wybór Teraz przy pisaniu programu można posługiwać się My_lib: void f () { My_lib::String s = "Byron"; // znajduje My_lib::His_string::String // ... } using namespace My_lib; void g (Vector<String> &vs) { // ... my_fct(vs[5]); // ... }

Komponowanie i wybór Gdy chcemy utworzyć interfejs i dołożyć do niego kilka nazw z innych interfejsów to stosujemy wybór za pomocą deklaracji użycia, na przykład: namespace My_string { using His_string::String; using His_string::operator+; // … } Deklaracja użycia wprowadza do zasięgu każdą deklarację o podanej nazwie. Pojedyncza deklaracja użycia może wprowadzić każdy wariant funkcji przeciążonej.

Komponowanie i wybór Łączenie komponowania (za pomocą dyrektyw użycia) z wyborem (za pomocą deklaracji użycia) zapewnia elastyczność potrzebną w praktyce. Z użyciem tych mechanizmów możemy zapewnić dostęp do wielu udogodnień, a zarazem rozwiązać problem konfliktu nazw i niejednoznaczności wynikających z komponowania. Nazwy zadeklarowane jawnie w przestrzeni nazw (łącznie z nazwami wprowadzonymi za pomocą deklaracji użycia) mają pierwszeństwo przed nazwami wprowadzonymi za pomocą dyrektyw użycia. Nazwę w nowej przestrzeni nazw można zmienić za pomocą instrukcji typedef lub poprzez dziedziczenie.

Przestrzenie nazw są otwarte Przestrzeń nazw jest otwarta, co oznacza, że można do niej dodawać nowe pojęcia w kilku deklaracjach (być może rozmieszczonych w różnych plikach), na przykład: namespace NS { int f (); // NS ma nową składową f() } namespace NS { int g (); // teraz NS ma dwie składowe f() i g() } Definiując wcześniej zadeklarowaną składową w przestrzeni nazw, bezpieczniej jest użyć operatora zakresu niż ponownie otwierać przestrzeń (kompilator nie wykryje literówek w nazwie składowej), na przykład: namespace NS { int h (); } int NS::hhh () // błąd - zamiast h napisano hhh { /*…*/ }

Przestrzeń nazw std W języku C++ wszystkie nazwy z biblioteki standardowej są umieszczone w przestrzeni nazw std. W języku C tradycyjnie używa się plików nagłówkowych i wszystkie nazwy w nich deklarowane są w przestrzeni globalnej (dostępne bez żadnych kwalifikacji). Aby zapewnić możliwość kompilowania przez kompilatory C++ programów napisanych w C przyjęto, że jeśli użyjemy tradycyjnej (pochodzącej z C) nazwy pliku nagłówkowego, to odpowiedni plik jest włączany i zadeklarowane w nim nazwy są dodawane do globalnej przestrzeni nazw. Jeśli natomiast ten sam plik nagłówkowy włączymy pod nową nazwą, to nazwy w nim deklarowane są dodawane do przestrzeni nazw std. Przyjęto przy tym konwencję, że pliki nagłówkowe z C nazwie nazwa.h są w C++ nazywane cnazwa (pary plików <math.h> i <cmath>, itp).

Zagnieżdżone przestrzenie nazw i klasy zagnieżdżone Wewnątrz przestrzeni naw można zdefiniowań inną przestrzeń. Klasa tworzy lokalną przestrzeń nazw – domyślną dla składowych w tej klasie. W definicji klasy można umieścić definicję innego typu: klasy, struktury, wyliczenia czy też typu nazwanego za pomocą instrukcji typedef.