C++ objektově orientovaná nadstavba programovacího jazyka C

Podobné dokumenty
C++ objektově orientovaná nadstavba programovacího jazyka C

Mělká a hluboká kopie

přetížení operátorů (o)

přetížení operátorů (o)

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

Jazyk C++, některá rozšíření oproti C

Pokročilé programování v jazyce C pro chemiky (C3220) Operátory new a delete, virtuální metody

Pokročilé programování v jazyce C pro chemiky (C3220) Pokročilá témata jazyka C++

C++ přetěžování funkcí a operátorů. Jan Hnilica Počítačové modelování 19

Programování v C++ 3, 3. cvičení

Základy C++ I. Jan Hnilica Počítačové modelování 18

Více o konstruktorech a destruktorech

Dědění, polymorfismus

PROGRAMOVÁNÍ V C++ URČENO PRO VZDĚLÁVÁNÍ V AKREDITOVANÝCH STUDIJNÍCH PROGRAMECH ROSTISLAV FOJTÍK

Abstraktní třídy, polymorfní struktury

Programování v C++ 1, 1. cvičení

Ukazatele, dynamická alokace

Programování v C++ 1, 6. cvičení

Přetěžování operátorů

Jazyk C++ I. Šablony 2

2) Napište algoritmus pro vložení položky na konec dvousměrného seznamu. 3) Napište algoritmus pro vyhledání položky v binárním stromu.

PB161 Programování v jazyce C++ Přednáška 10

Pokročilé programování v jazyce C pro chemiky (C3220) Třídy v C++

Funkční objekty v C++.

ZPRO v "C" Ing. Vít Hanousek. verze 0.3

Polymorfismus. Časová náročnost lekce: 3 hodiny Datum ukončení a splnění lekce: 30.března

Obsah. Předmluva 13 Zpětná vazba od čtenářů 14 Zdrojové kódy ke knize 15 Errata 15

Syntaxe vyjímek. #include <iostream> #include <string> using namespace std; // Trida vyjimek class Vyjimka { private:

7 Formátovaný výstup, třídy, objekty, pole, chyby v programech

Programování v jazyce C a C++

PROGRAMOVÁNÍ V C++ CVIČENÍ

Zápis programu v jazyce C#

Jazyk C++ 1. Blok 3 Objektové typy jazyka C++ Třída. Studijní cíl. Doba nutná k nastudování. Průvodce studiem

PREPROCESOR POKRAČOVÁNÍ

Chování konstruktorů a destruktorů při dědění

PŘETĚŽOVÁNÍ OPERÁTORŮ

Př. další použití pointerů

Jazyk C++ I. Šablony

Programování v C++ 2, 4. cvičení

Úvod do programovacích jazyků (Java)

8 Třídy, objekty, metody, předávání argumentů metod

Pokročilé programování v jazyce C pro chemiky (C3220) Statické proměnné a metody, šablony v C++

Jazyk C++ I. Polymorfismus

PB161 Programování v jazyce C++ Přednáška 9

Zpracoval:

IUJCE 07/08 Přednáška č. 6

Program převod z desítkové na dvojkovou soustavu: /* Prevod desitkove na binarni */ #include <stdio.h>

PB161 Programování v jazyce C++ Přednáška 4

Úvod do programování. Lekce 1

Odvozené a strukturované typy dat

<surface name="pozadi" file="obrazky/pozadi/pozadi.png"/> ****************************************************************************

Objektově orientované programování

Generické programování

Programování v C++ 1, 5. cvičení

Základy jazyka C# Obsah přednášky. Architektura.NET Historie Vlastnosti jazyka C# Datové typy Příkazy Prostory jmen Třídy, rozhraní

Dědičnost. Časová náročnost lekce: 3 hodiny Datum ukončení a splnění lekce: 23.března

Obsah přednášky 7. Základy programování (IZAPR) Přednáška 7. Parametry metod. Parametry, argumenty. Parametry metod.

Vector datový kontejner v C++.

Martin Flusser. Faculty of Nuclear Sciences and Physical Engineering Czech Technical University in Prague. October 17, 2016

PB161 Programování v jazyce C++ Přednáška 4

Programování v C++ VI

PB161 Programování v jazyce C++ Přednáška 10

PROMĚNNÉ, KONSTANTY A DATOVÉ TYPY TEORIE DATUM VYTVOŘENÍ: KLÍČOVÁ AKTIVITA: 02 PROGRAMOVÁNÍ 2. ROČNÍK (PRG2) HODINOVÁ DOTACE: 1

Objektov orientované programování. C++ Akademie SH. 7. Objektov orientované programování. Michal Kvasni ka. Za áte níci C++ 2.

konstruktory a destruktory (o)

Úvod do programovacích jazyků (Java)

Třídy a struktury v C++

Virtuální metody - polymorfizmus

VÝUKOVÝ MATERIÁL. Bratislavská 2166, Varnsdorf, IČO: tel Číslo projektu

V dalších letech se pak začaly objevovat první normy pro jazyk C++ (ISO/IEC 14882:1998; ISO/IEC 9899:1999; ISO/IEC 14882:2003; ISO/IEC 14882:2011).

Programování v C++ 1, 17. cvičení

Dynamická identifikace typů v C++.

TŘÍDY POKRAČOVÁNÍ. Události pokračování. Příklad. public delegate void ZmenaSouradnicEventHandler (object sender, EventArgs e);

10. března 2015, Brno Připravil: David Procházka. Programovací jazyk C++

for (int i = 0; i < sizeof(hodnoty) / sizeof(int); i++) { cout<<hodonoty[i]<< endl; } cin.get(); return 0; }

PB přednáška (23. listopadu 2015)

Programování v jazyce C a C++

Objektové programování

Konstruktory a destruktory

Základy programování (IZP)

Práce s polem a pamětí

Jazyk C++ II. Šablony a implementace

1. Dědičnost a polymorfismus

Množina čísel int stl-set-int.cpp

Jazyk C++ II. Výjimky

Osnova přednášky. Programové prostředky řízení Úvod do C# II. Přístup ke členům. Členy (Members)

C++ Akademie SH. 2. Prom nné, podmínky, cykly, funkce, rekurze, operátory. Michal Kvasni ka. 20. b ezna Za áte níci C++

Objektově orientované programování. Úvod

Základy programování (IZP)

Základy programování (IZP)

Pokročilé programování v jazyce C pro chemiky (C3220) Dědičnost tříd v C++

Zpracoval:

Připravil: David Procházka. Programovací jazyk C++

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

IB111 Programování a algoritmizace. Objektově orientované programování (OOP)

PB161 Programování v jazyce C++ Přednáška 8

SYSTÉMOVÉ PROGRAMOVÁNÍ Cvičení č.1

Počítačové laboratoře bez tajemství aneb naučme se učit algoritmizaci a programování s využitím robotů CZ.1.07/1.3.12/

for (i = 0, j = 5; i < 10; i++) { // tělo cyklu }

Preprocesor. Karel Richta a kol. katedra počítačů FEL ČVUT v Praze. Karel Richta, Martin Hořeňovský, Aleš Hrabalík, 2016

Transkript:

C++ objektově orientovaná nadstavba programovacího jazyka C (2. část) Josef Dobeš Katedra radioelektroniky (13137), blok B2, místnost 722 dobes@fel.cvut.cz 18. května 2015 České vysoké učení technické v Praze, Fakulta elektrotechnická 1

1 Dynamická alokace proměnných, struktur a poĺı Dynamická alokace proměnných (nejčastěji struktur) a jejich poĺı se v C++ nejčastěji provádí pomocí operátorů new, delete a delete [ ]. Při dynamickém alokování zejména rozsáhlých datových struktur je však třeba pracovat s určitou ideou obsazování paměti, jinak pamět ové nároky programu prudce rostou. Ještě častější chybou je neuvolňování dříve dynamicky alokované paměti anebo její nesprávné uvolňování, zejména nesprávné uvolňování dynamicky alokovaných poĺı. V následujícím programu bude demonstrováno nesprávné i správné uvolňování dynamicky alokovaných struktur. Pro jednoduchost je zvolena pouze jednoduchá struktura obsahující dvě proměnné float a příkazem typedef je definován ukazatel na tuto strukturu. Po prvním řádku programu main máme definovány dva ukazatele na deklarovanou strukturu, není však dané to, na co ukazují. Tuto situaci můžeme znázornit následujícím způsobem: Operátor new dává manažeru systémové paměti následující dvě instrukce: Alokuje potřebnou pamět (její velikost závisí na typu specifikovaném za operátorem) Uschová adresu v paměti do ukazatele, čímž ukazatel označuje umístění v paměti 2

#include <iostream> struct test_score { float midterm, final; ; typedef test_score * test_pointer; int main() { test_pointer score_ptr, another_score_ptr; score_ptr = new test_score; score_ptr->midterm = 99.0; score_ptr->final = 95.0; another_score_ptr = score_ptr; score_ptr = new test_score; score_ptr->midterm = 40.0; score_ptr->final = 35.0; std::cout << "1st midterm mark = " << another_score_ptr->midterm << ", 1st final mark = " << another_score_ptr->final << std::endl; std::cout << "2nd midterm mark = " << score_ptr->midterm << ", 2nd final mark = " << score_ptr->final; another_score_ptr = NULL; // nesprávný způsob likvidace ukazatele delete score_ptr; // správný způsob likvidace // implicitně je nacrácena hodnota (int)0 3

Po aplikaci prvního příkazu new je už jasná poloha struktury v paměti, nejsou však dosud definovány jednotlivé její prvky: Po následujících dvou přiřazovacích příkazech však již jsou tyto prvky definovány: a po přiřazení jednoho ukazatele druhému oba dva ukazatele označují stejné místo v paměti: 4

Po aplikaci druhého příkazu new a následujících dvou přiřazujících příkazů již oba ukazatelé označují jiné místo v paměti a taktéž obsahy obou struktur jsou již různé: Následující dva příkazy výstupu tuto skutečnost potvrzují: 1st midterm mark = 99, 1st final mark = 95 2nd midterm mark = 40, 2nd final mark = 35 Následující přiřazení nulového pointeru NULL je typickou a častou chybou v paměti zůstanou prvky struktury, které však již nejsou dosažitelné: Správný způsob likvidace struktury je proveden příkazem delete, po němž jsou pamět ová místa pro prvky struktury uvolněna: 5

2 Operátor delete [ ] Při uvolňování dynamicky alokovaného pole se používá také operátor delete, za který ovšem musíme přidat prázdné indexové závorky: delete [ ] array; Při uvolňování dynamicky alokovaného pole objektů se pro každý z prvků zavolá destruktor. Důležité poznámky: Pokud při uvolňování pole neuvedeme za kĺıčovým slovem závorky [ ], překladač nebude hlásit chybu. V případě pole objektů se ale zavolá destruktor pouze pro první prvek pole a záleží na konkrétní struktuře programu, co to způsobí. U některých (především starších) překladačů jazyka C++ je nutné do lomených závorek za delete připsat skutečný počet prvků (týká se to např. starších verzí překladače Borland C++) 6

3 Nezdařená alokace dynamické proměnné Norma ANSI/ISO jazyka C++ stanoví, že operátor new v případě neúspěchu (v typickém případě nedostatku paměti) vyvolá výjimku bad alloc. Výjimku lze tedy ošetřit pomocí bloků try a catch: try // v ANSI C++ { pld = new long double; catch(bad_alloc) { // řešení situace Chceme-li se vyhnout práci s výjimkami, použijeme operátor new s dodatečným parametrem nothrow: // v ANSI C++ pld = new(nothrow) long double; if (!pld) error(); 7

4 Předávání odkazem V jazyce C++ můžeme také předávat parametry funkcí odkazem. Před parametr, který chceme předat odkazem, musíme napsat znak &. V těle funkce se s takovým parametrem pracuje stejně jako s parametrem předaným hodnotou (použije se tzv. reference). Jazyk C++ je tedy v tomto ohledu bližší jazyku Fortran než jazyk C. Následující program přehodí obě proměnné: #include <stdio.h> void swap(int &a, int &b) { int c = a; a = b; b = c; int main() { int a = 5, b = 7; printf("%d %d\n", a, b); swap(a, b); printf("%d %d", a, b); V jazyce C bychom přehození obou proměnných museli řešit pomocí ukazatelů. 8

5 Odvozené třídy class student { public : enum year {junior, senior, grad; student(char *nm, int id); void print() const; protected : int student_id; char name[30]; ; class grad_student : public student { public : enum support {fellowship, other; grad_student(char *nm, int id, year x); void print() const; protected : char thesis[80]; ; V tomto příkladu je grad student odvozená třída a student je základní třída. Užitím kĺıčového slova public za dvojtečkou v záhlaví odvozené třídy říkáme, že členy protected a public ve třídě student jsou děděny jako protected a public do třídy grad student. Privátní členy nejsou dostupné. Nyní každý graduate student je student, ale student nemusí být graduate student. 9

6 Redefinice unárního operátoru V následujících dvou příkladech bude redefinován unární operátor ++ tak, aby byl použitelný pro správné sčítání vteřin, minut, hodin a dní. 6.1 Použití funkce třídy V tomto případě je aritmetika správného sčítání vteřin, minut, hodin a dní naprogramována funkcí třídy operator ++(), která používá další funkci třídy tick() a vrací data třídy pomocí vždy vytvořeného ukazatele this. Správná aritmetika je zajištěna výrazy konstruktoru a výpis dní, hodin, minut a vteřin zajišt uje funkce print(), která nemodifikuje data třídy. #include <iostream> #include <iomanip> using namespace std; class clock { public : inline clock(unsigned long i); //konstruktor a konverze na dny, hodiny, void print() const; //formátovaný tiskový výstup, nemodifikuje objekt void tick(); //přidá jednu vteřinu, tato tedy není const clock operator ++() { tick(); return *(this); //this ukazuje na objekt private : unsigned long tot_secs, secs, mins, hours, days; ; 10

inline clock::clock(unsigned long i) { //konstruktor nemá typ tot_secs = i; secs = tot_secs % 60; mins = (tot_secs / 60) % 60; hours = (tot_secs / 3600) % 24; days = tot_secs / 86400; void clock::tick() { clock temp = clock(++tot_secs); secs = temp.secs; mins = temp.mins; hours = temp.hours; days = temp.days; void clock::print() const { cout << setw(4) << days << " d:" << setw(2) << hours << " h:" << setw(2) << mins << " m:" << setw(2) << secs << " s" << endl; int main() { clock t1(59), t2(172799); // = dva dny minus jedna vteřina cout << "Pocatecni casy jsou..." << endl; t1.print(); t2.print(); ++t1; ++t2; cout << "Po pricteni jedne sekundy..." << endl; t1.print(); t2.print(); 11

6.2 Použití separátní funkce V tomto případě je unární operátor ++ redefinován pomocí funkce, která není členem třídy. První část programu je proto mírně odlišná od předcházející verze, druhá část programu je však přesně stejná jako předcházející strana, tj. clock unchanged.cpp (viz též dole). #include <iostream> #include <iomanip> using namespace std; class clock { public : inline clock(unsigned long i); void print() const; void tick(); private : unsigned long tot_secs, secs, mins, hours, days; ; /* Definice funkcí, která není členem třídy */ clock operator ++(clock& c) { //je využito odkazu c.tick(); return c; #include "clock unchanged.cpp" 12

6.2.1 Prefixový a postfixový operátor //Prefixová a postfixová forma přetížení operátoru ++: //Upravená verze Richarda Vachuly tak, aby fungovala pod Digital Mars i GCC #include <iostream> class Foo { int value; public : //Konstruktor: Foo(int val = 0) : value(val) { //Digital Mars musí val mít v () ne v { //Přetížení unárních operátorů Foo& operator++(); //prefix Foo operator++(int); //postfix //Přetížení binárního operátoru friend std::ostream& operator<<(std::ostream& outp, const Foo& f); ; Foo& Foo::operator++() { //unary prefix this->value++; return *this; Foo Foo::operator++(int) { //unary postfix Foo temp(*this); ++(*this); return temp; std::ostream& operator<<(std::ostream& outp, const Foo& f) { //binary return outp << f.value; 13

int main() { Foo foo(5); // std::cout << foo << std::endl; std::cout << ++foo << std::endl; std::cout << foo << std::endl; std::cout << foo++ << std::endl; std::cout << foo << std::endl; // return 0; dává očekávaný výsledek 5 6 6 6 7 7 Virtuální funkce Virtuální funkce umožňují v hierarchii tříd vytváření mimořádného polymorfismu. Třída, která obsahuje pouze virtuální funkce se nazývá abstraktní třída (takovou třídu lze deklarovat, ale ne definovat objekt této třídy). 14

V následujícím příkladu máme jednu abstraktní bázovou třídu, která obsahuje jednu standardní virtuální funkci a jednu čistou ( pure ) virtuální funkci. Čistá virtuální funkce musí být ve všech dceřiných třídách definována, zatímco u standardní virtuální funkce lze v odvozených třídách použít její implicitní ( default ) chování definované v rodičovské třídě. 15

#include <iostream> using namespace std; // kvúli cout const double PI = 3.14159; // zde nejde o přesnost výpočtu class shape { // abstraktní bázová třída, obsahuje pouze virtuální funkce public : virtual double area() const { return 0; // definováno "default" chování virtual char *get_name() = 0; // čistě virtuální funkce "pure" virtual protected : double x, y; // Digital Mars i GNU GCC fungují dobře i bez tohoto řádku ; class rectangle : public shape { // "Obdélník" odvozený z abstraktní třídy public : rectangle(double h, double w) : height(h), width(w) { // přiřazení délek double area() const { return height * width; char *get_name() { return (char *)"Obdelnik: "; // pro Digital Mars C++ private : // (char *) není nutné double height, width; // pro GNU GCC warning ; class circle : public shape { // "Kruh" odvozený z abstraktní třídy public : circle(double r) : radius(r) { // přiřazení poloměru double area() const { return PI * radius * radius; char *get_name() { return (char *)"Kruh: "; private : double radius; ; 16

Kromě funkcí tříd popisujících chování obdélníku a kruhu vytvoříme i jednodušší odvozenou funkci pro úsečku, která pro výpočet obsahu použije implicitní chování funkce rodičovské třídy. Čistě virtuální funkce však musí být definována. class line : public shape { // zde je použit implicitní konstruktor public : char *get_name() { return (char *)"Usecka: "; // musí být, jde o pure ; // bylo použito default chování pro výpo čet plochy, tj. nulová plocha Hlavní program pak jednoduše pracuje se všemi třemi tvary (obdélníkem, kruhem i úsečkou): int main() { shape *ptr_shape; //shape something; // chyba abstraktní bázovou t řídu nelze vytvořit přímo rectangle rec(4, 5.5); // toto je přímá inicializace výšky a šířky circle cir(10); // zde je přímá inicializace jen pro poloměr kruhu line lin; cout << "Tento program pouziva hierarchie trid pro tvary...\n" ; ptr_shape = &rec; cout << ptr_shape->get_name() << "plocha = " << ptr_shape->area() << '\n'; ptr_shape = &cir; cout << ptr_shape->get_name() << "plocha = " << ptr_shape->area() << '\n'; ptr_shape = &lin; cout << ptr_shape->get_name() << "plocha = " << ptr_shape->area(); // Digital Mars i GNU GCC vracejí implicitn ě nulu, tj. return nemusí být 17

Pokud bychom se pokusili definovat objekt rodičovské třídy, která je abstraktní, dostali bychom chybové hlášení: shape something; ^ virtual.cpp(40) : Error: cannot create instance of abstract class 'shape' --- errorlevel 1 Chybové hlášení bychom rovněž obdrželi, pokud bychom v odvozené třídě line nedefinovali funkci get name(): line lin; ^ virtual.cpp(43) : Error: cannot create instance of abstract class 'line' --- errorlevel 1 Nakonec uvedeme výstup programu pracujícího s virtuálními funkcemi: Tento program pouziva hierarchie trid pro tvary... Obdelnik: plocha = 22 Kruh: plocha = 314.159 Usecka: plocha = 0 18

8 Šablony Dalším nástrojem umožňujícím značný polymorfismus jsou šablony. Pomocí šablon můžeme vytvořit kód, který je použitelný pro různé datové typy. V následujícím příkladu jsou demonstrovány nejrůznější způsoby vzájemných záměn. Několik datových typů můžeme zaměňovat pomocí šablony, řetězce je však třeba zaměňovat pomocí zvláštní funkce se stejným jménem: #include <iostream> #include <string.h> // Digital Mars připouští i #include <string> tj. bez h #include <complex> //using namespace std; // kvůli rozlišení swap a std::swap NELZE zde použít template < class T > void swap(t& x, T& y) { T temp; temp = x; x = y; y = temp; void swap(char *s1, char *s2) { int max_len = (strlen(s1) > strlen(s2))? strlen(s1) : strlen(s2); char *temp = new char[max_len + 1]; strcpy(temp, s1); strcpy(s1, s2); strcpy(s2, temp); delete [] temp; 19

V hlavním programu pak vyzkoušíme širokou škálu záměn nejrůznějších datových typů: int main() { int k = 5, l = 9; std::complex < double > a = 3.14, b = 8; // pro jeden reálný argument je % // definováno v _complex.h jako přiřazení reálné části, imaginární bude 0 std::complex < float > c = 4, d = 7; // komplexní proměnné poloviční délky char str1[6] = "Brno", str2[6] = "Praha"; // stejná délka [6] kvůli záměně /*"Záměna proměnných int"*/ swap(k, l); // přímá záměna dvou proměnných int první funkcí std::cout << k << " " << l << std::endl; // i u endl se musí uvádět std:: /*"Záměna řetězců"*/ swap(str1, str2); // vyvolá se druhá definovaná funkce záměny řetězců std::cout << str1 << " " << str2 << std::endl; /*"Záměna prvního znaku v řetězci"*/ swap(*str1, *str2); // přímá záměna prvního prvku pole ([0]) první funkcí std::cout << str1[0] << " " << str2[0] << std::endl; // *str1 == str1[0] /*"Záměna komplexních proměnných"*/ std::swap(a, b); // swap je člen namespace std, swap bez std nelze použít std::cout << a << " " << b << ", velikost a = " << sizeof(a) << std::endl; std::swap(c, d); std::cout << c << " " << d << ", velikost c = " << sizeof(c); 20

Komplexní čísla je však nutné vzájemně vyměňovat pomocí std::swap; pokud bychom použili jen funkci swap, dostaneme chybové hlášení: swap(c, d); ^ template.cpp(41) : Error: ambiguous reference to symbol Had: swap(t&,t&) and: std::swap(_tp&,_tp&) --- errorlevel 1 Výstup tohoto programu demonstrujícího záměny dat pomocí šablon je tedy následující: 9 5 Praha Brno B P (8,0) (3.14,0), velikost a = 16 (7,0) (4,0), velikost c = 8 21

Obsah 1 Dynamická alokace proměnných, struktur a poĺı 2 2 Operátor delete [ ] 6 3 Nezdařená alokace dynamické proměnné 7 4 Předávání odkazem 8 5 Odvozené třídy 9 6 Redefinice unárního operátoru 10 6.1 Použití funkce třídy.............................. 10 6.2 Použití separátní funkce............................ 12 6.2.1 Prefixový a postfixový operátor.................... 13 7 Virtuální funkce 14 8 Šablony 19 22