Mělká a hluboká kopie

Podobné dokumenty
Více o konstruktorech a destruktorech

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

Abstraktní třídy, polymorfní struktury

Abstraktní datové typy

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

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

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

Konstruktory a destruktory

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

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

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

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

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

Programování II. Návrh programu I 2018/19

Jazyk C++ I. Šablony 2

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

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

Pole, množina, tabulka

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

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

Objektově orientované programování. Úvod

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

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

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

Jazyk C++ I. Šablony

Ukazatele, dynamická alokace

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

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

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

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

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

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

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

Jazyk C++ I. Šablony 3

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

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.

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

konstruktory a destruktory (o)

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

Dědění, polymorfismus

Vector datový kontejner v C++.

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

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

Dynamická identifikace typů v C++.

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

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

Jazyk C++ II. Šablony a implementace

Přetěžování operátorů, dynamika objektů 2

PREPROCESOR POKRAČOVÁNÍ

int ii char [16] double dd název adresa / proměnná N = nevyužito xxx xxx xxx N xxx xxx N xxx N

Principy objektově orientovaného programování

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

Virtuální metody - polymorfizmus

Odvozené a strukturované typy dat

Jazyk C# a platforma.net

PB přednáška (12. října 2015)

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).

Spojová implementace lineárních datových struktur

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

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

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

Úvod do programovacích jazyků (Java)

Dynamika objektů. Karel Richta a kol. katedra počítačů FEL ČVUT v Praze

Jazyk C++ I. Polymorfismus

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

Funkční objekty v C++.

Další příklady. Katedra softwarového inženýrství. Katedra teoretické informatiky, Fakulta informačních technologii, ČVUT v Praze. Karel Müller, 2011

Pole a kolekce. v C#, Javě a C++

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

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

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

Programování v jazyce C a C++

Operační systémy. Cvičení 4: Programování v C pod Unixem

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

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

Zpracoval:

Základy programování (IZP)

Základy programování (IZP)

Třídy a struktury v C++

Dynamická alokace paměti

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

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

Úvod do programovacích jazyků (Java)

IAJCE Přednáška č. 8. double tprumer = (t1 + t2 + t3 + t4 + t5 + t6 + t7) / 7; Console.Write("\nPrumerna teplota je {0}", tprumer);

Programování v C++ VI

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

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

Generické programování

24. listopadu 2013, Brno Připravil: David Procházka

BI-PA1 Programování a algoritmizace 1 Katedra teoretické informatiky

Standardní algoritmy v C++.

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

Rozptylování, stromy - zákl. vlastnosti

Zpracoval:

Jazyk C++ I. Polymorfismus

KTE / ZPE Informační technologie

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

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

Martin Flusser. December 15, 2016

Programování v jazyce C a C++

Transkript:

Karel Müller, Josef Vogel (ČVUT FIT) Mělká a hluboká kopie BI-PA2, 2011, Přednáška 5 1/28 Mělká a hluboká kopie Ing. Josef Vogel, CSc Katedra softwarového inženýrství Katedra teoretické informatiky, Fakulta informačních technologii, ČVUT v Praze Karel Müller, Josef Vogel, 2011 Programování a algoritmizace 2, BI-PA2, 2011, Přednáška 5 BI-PA2 Evropský sociální fond Praha & EU: Investujeme do vaší budoucnosti

Mělká a hluboká kopie, počítané reference třída Pole s přetíženým operátorem indexování problémy s přiřazením mezi objekty typu Pole (mělká kopie) hluboká kopie pomocí přetíženého operátoru přiřazení kopírující konstruktor třída Pole s použitím techniky počítaných referencí Karel Müller, Josef Vogel (ČVUT FIT) Mělká a hluboká kopie BI-PA2, 2011, Přednáška 5 2/28

p5\pole1\main.cpp Dynamické pole Karel Müller, Josef Vogel (ČVUT FIT) Mělká a hluboká kopie BI-PA2, 2011, Přednáška 5 3/28 Napíšeme třídu pro realizaci dynamického pole (počet prvků bude dán parametrem konstruktoru), ve které přístup k prvkům bude umožněn operátorem indexování Příklad použití: #include "pole.h" počet prvků pole (může být dán proměnnou) int main() { Pole a(5); for (int i=0; i<a.delka(); i++) a[i] = i; cout << "pole a: " << a << endl; // [0 1 2 3 4] int soucet = 0; for (int i=0; i<a.delka(); i++) soucet += a[i]; cout << "soucet prvku pole je " << soucet << endl; system("pause"); return 0; přetížený operátor indexování

p5\pole1\pole.h Deklarace třídy Pole Karel Müller, Josef Vogel (ČVUT FIT) Mělká a hluboká kopie BI-PA2, 2011, Přednáška 5 4/28 typedef int TypPrvku; class Pole { TypPrvku *pole; int pocetprvku; public: explicit Pole(int pp); ~Pole(); int delka() const {return pocetprvku; TypPrvku& operator[](int); const TypPrvku& operator[](int) const; přetížení operátoru indexování pro proměnný objekt typu Pole friend ostream& operator<<(ostream&, const Pole&); ; přetížení operátoru indexování pro konstantní objekt typu Pole

p5\pole1\pole.cpp Definice metod třídy Pole Karel Müller, Josef Vogel (ČVUT FIT) Mělká a hluboká kopie BI-PA2, 2011, Přednáška 5 5/28 Pole::Pole(int pp) { pocetprvku = pp; pole = new TypPrvku[pocetPrvku]; for (int i=0; i<pocetprvku; i++) pole[i] = TypPrvku(); Pole::~Pole() { delete [] pole; pole = NULL; pocetprvku = 0;

p5\pole1\pole.cpp Definice metod třídy Pole Karel Müller, Josef Vogel (ČVUT FIT) Mělká a hluboká kopie BI-PA2, 2011, Přednáška 5 6/28 TypPrvku& Pole::operator[](int ind) { if (ind<0 ind>=pocetprvku) throw "nedovoleny index"; return pole[ind]; const TypPrvku& Pole::operator[](int ind) const { if (ind<0 ind>=pocetprvku) throw "nedovoleny index"; return pole[ind]; ostream& operator<<(ostream& o, const Pole& p) { for (int i=0; i<p.pocetprvku; i++) o << p.pole[i] << " "; return o;

p5\pole1\main2.cpp Přiřazení mezi objekty typu Pole Podívejme se na funkci main2 int main() { Pole a(5); for (int i=0; i<a.delka(); i++) a[i] = i; cout << "pole a: " << a << endl; // [0 1 2 3 4] Pole b(3); b = a; b[1] = 10; cout << "pole a: " << a << endl; // [0 10 2 3 4] cout << "pole b: " << b << endl; // [0 10 2 3 4] system("pause"); return 0; Proč se změní též a[1], když jsme přiřazovacím příkazem změnili b[1]? Odpověď na dalším slajdu Karel Müller, Josef Vogel (ČVUT FIT) Mělká a hluboká kopie BI-PA2, 2011, Přednáška 5 7/28

Karel Müller, Josef Vogel (ČVUT FIT) Mělká a hluboká kopie BI-PA2, 2011, Přednáška 5 8/28 Přiřazení mezi objekty typu Pole mělká kopie a 5 0 1 2 3 4 b = a; a 5 0 1 2 3 4 b 5 b[1] = 10; a 5 0 10 2 3 4 b 5

Přiřazení mezi objekty typu Pole mělká kopie Přiřazení mezi objekty stejného typu (které vygeneruje překladač) probíhá tak, že se zkopíruje bitový obsah položek To pro položky typu ukazatel na dynamicky alokovaná data znamená, že oba objekty ukazují na stejná, dynamicky alokovaná data Tento způsob přiřazení mezi objekty obsahujícími ukazatele na dynamicky alokovaná data se nazývá mělká kopie Mělká kopie by nevadila, když o ní víme Problém je s destruktorem Karel Müller, Josef Vogel (ČVUT FIT) Mělká a hluboká kopie BI-PA2, 2011, Přednáška 5 9/28

p5\pole2\pole.cpp Přiřazení mezi objekty typu Pole mělká kopie Karel Müller, Josef Vogel (ČVUT FIT) Mělká a hluboká kopie BI-PA2, 2011, Přednáška 5 10/28 Upravme destruktor třídy Pole tak, aby vypsal adresu pole, které dealokuje Pole::~Pole() { cout << "dealokace pole na adrese " << (void*)pole << endl; delete [] pole; pole = NULL; pocetprvku = 0;

p5\pole2\main.cpp Přiřazení mezi objekty typu Pole mělká kopie Karel Müller, Josef Vogel (ČVUT FIT) Mělká a hluboká kopie BI-PA2, 2011, Přednáška 5 11/28 A použijme v této hlavní funkci: void funkce() { Pole a(5); for (int i=0; i<a.delka(); i++) a[i] = i; cout << "pole a: " << a << endl; // [0 1 2 3 4] Pole b(3); b = a; b[1] = 10; cout << "pole a: " << a << endl; // [0 10 2 3 4] cout << "pole b: " << b << endl; // [0 10 2 3 4] /* zde vypise napr. dealokace pole na adrese 0x3d3e90 dealokace pole na adrese 0x3d3e90 */ int main() { funkce(); system("pause"); return 0; to je špatně, protože nelze vícekrát dealokovat paměť se stejnou adresou

Karel Müller, Josef Vogel (ČVUT FIT) Mělká a hluboká kopie BI-PA2, 2011, Přednáška 5 12/28 Přiřazení mezi objekty typu Pole hluboká kopie Při přiřazení x = y mezi objekty typu Pole musíme nejprve dealokovat pole, na které ukazuje objekt x, a pak dynamicky vytvořit kopii pole, na které ukazuje objekt y a b 5 3 0 1 2 3 4 0 0 0 b = a; a 5 0 1 2 3 4 b 5 0 1 2 3 4 dynamická kopie 0 0 0 dealokováno

p5\pole3\pole.h, pole.cpp Třída Pole s přetíženým operátorem = Karel Müller, Josef Vogel (ČVUT FIT) Mělká a hluboká kopie BI-PA2, 2011, Přednáška 5 13/28 class Pole { TypPrvku *pole; int pocetprvku; public: explicit Pole(int pp); ~Pole(); int delka() const {return pocetprvku; TypPrvku& operator[](int); const TypPrvku& operator[](int) const; Pole& operator=(const Pole&); friend ostream& operator<<(ostream&, const Pole&); ; Pole& Pole::operator=(const Pole& p) { if (&p==this) return *this; delete [] pole; pocetprvku = p.pocetprvku; pole = new TypPrvku[pocetPrvku]; for (int i=0; i<pocetprvku; i++) pole[i] = p.pole[i]; return *this;

p5\pole3\main.cpp Třída Pole s přetíženým operátorem = Karel Müller, Josef Vogel (ČVUT FIT) Mělká a hluboká kopie BI-PA2, 2011, Přednáška 5 14/28 Použití: #include "pole.h" int main() { Pole a(5); for (int i=0; i<a.delka(); i++) a[i] = i; cout << "pole a: " << a << endl; // [0 1 2 3 4] Pole b(3); b = a; b[1] = 10; cout << "pole a: " << a << endl; // [0 1 2 3 4] cout << "pole b: " << b << endl; // [0 10 2 3 4] system("pause"); return 0;

p5\pole3\main2.cpp Třída Pole s přetíženým operátorem = Karel Müller, Josef Vogel (ČVUT FIT) Mělká a hluboká kopie BI-PA2, 2011, Přednáška 5 15/28 Podívejme se ještě na další příklad funkce main #include "pole.h" int main() { Pole a(5); toto není přiřazení, ale deklarace objektu doplněná inicializací jiným objektem for (int i=0; i<a.delka(); i++) a[i] = i; cout << "pole a: " << a << endl; // [0 1 2 3 4] Pole b = a; b[1] = 10; cout << "pole a: " << a << endl; // [0 10 2 3 4] cout << "pole b: " << b << endl; // [0 10 2 3 4] system("pause"); return 0; a je to tu zas!

Kopírující konstruktor Deklarací Pole b = a; se vytvoří objekt b a inicializuje se kopíí objektu a Tuto deklaraci a inicializaci převede překladač na deklaraci Pole b(a); ve které se volá tzv. kopírující konstruktor Kopírující konstruktor pro třídu T je konstruktor s jedním parametrem typu const T&, pro třídu Pole má tedy deklaraci Pole(const Pole&); Pokud kopírující konstruktor ve třídě deklarován není, překladač ho vytvoří s tím, že provede mělkou kopii Pokud mělká kopie nám nevyhovuje, musíme kopírující konstruktor deklarovat a definovat sami Poznámka: kopírující konstruktor se volá i při předání objektu jako parametru (pokud parametr není typu reference) Karel Müller, Josef Vogel (ČVUT FIT) Mělká a hluboká kopie BI-PA2, 2011, Přednáška 5 16/28

p5\pole4\pole.h, pole.cpp Třída Pole s kopírujícím konstruktorem Karel Müller, Josef Vogel (ČVUT FIT) Mělká a hluboká kopie BI-PA2, 2011, Přednáška 5 17/28 class Pole { TypPrvku *pole; int pocetprvku; public: explicit Pole(int pp); Pole(const Pole&); ~Pole(); int delka() const {return pocetprvku; TypPrvku& operator[](int); const TypPrvku& operator[](int) const; Pole& operator=(const Pole&); friend ostream& operator<<(ostream&, const Pole&); ; Pole::Pole(const Pole& p) { pocetprvku = p.pocetprvku; pole = new TypPrvku[pocetPrvku]; for (int i=0; i<pocetprvku; i++) pole[i] = p.pole[i];

Karel Müller, Josef Vogel (ČVUT FIT) Mělká a hluboká kopie BI-PA2, 2011, Přednáška 5 18/28 Počítané reference Sdílení dynamicky alokovaných dat lze vyřešit také technikou počítaných referencí. Počítané reference pro třídu Pole kopírující konstruktor Pole a(5); Pole CRPole a... Pole b=a; citacref 5 1 a b 5 2 objekt b se připojí na dynamicky alokovaná data, na které je připojen objekt a

Počítané reference Počítané reference pro třídu Pole přiřazení Pole a(5), b(3); a b b = a; 5 1 3 1 a 5 2 vrátí se do volné paměti b nejprve odpojení, pak připojení 0 Karel Müller, Josef Vogel (ČVUT FIT) Mělká a hluboká kopie BI-PA2, 2011, Přednáška 5 19/28

Počítané reference Počítané reference pro třídu Pole přiřazení prvku pole a 5 2 b b[1] = 10; a 5 2 b cout << a[1]; // vypíše 10 dynamicky alokovaná data jsou sdílena Karel Müller, Josef Vogel (ČVUT FIT) Mělká a hluboká kopie BI-PA2, 2011, Přednáška 5 20/28

p5\pole5\pole.h Počítané reference Karel Müller, Josef Vogel (ČVUT FIT) Mělká a hluboká kopie BI-PA2, 2011, Přednáška 5 21/28 typedef int TypPrvku; class Pole { public: explicit Pole(int pp); Pole(const Pole&); ~Pole(); int delka() const {return ptr->pocetprvku; TypPrvku& operator[](int); const TypPrvku& operator[](int) const; Pole& operator=(const Pole&); friend std::ostream& operator<< (std::ostream&, const Pole&); private:...

p5\pole5\pole.h Počítané reference Karel Müller, Josef Vogel (ČVUT FIT) Mělká a hluboká kopie BI-PA2, 2011, Přednáška 5 22/28 private: struct CRPole { TypPrvku *pole; int pocetprvku; int citacref; CRPole(int pp); ~CRPole(); ; CRPole *ptr; void pripoj(crpole *p); void odpoj(); ;

p5\pole5\pole.cpp Počítané reference Karel Müller, Josef Vogel (ČVUT FIT) Mělká a hluboká kopie BI-PA2, 2011, Přednáška 5 23/28 Pole::CRPole::CRPole(int d) { pole = new TypPrvku[d]; pocetprvku = d; citacref = 1; for (int i=0; i<pocetprvku; i++) pole[i] = TypPrvku(); Pole::CRPole::~CRPole() { delete [] pole;

p5\pole5\pole.cpp Počítané reference Karel Müller, Josef Vogel (ČVUT FIT) Mělká a hluboká kopie BI-PA2, 2011, Přednáška 5 24/28 TypPrvku& Pole::operator[](int ind) { if (ind<0 ind>=ptr->pocetprvku) throw "nedovoleny index"; return ptr->pole[ind]; const TypPrvku& Pole::operator[](int ind) const { if (ind<0 ind>=ptr->pocetprvku) throw "nedovoleny index"; return ptr->pole[ind]; ostream& operator<<(ostream& o, const Pole& p) { int n = p.delka(); for (int i=0; i<n; i++) o << p.ptr->pole[i] << ' '; o << endl; return o;

p5\pole5\pole.cpp Počítané reference Karel Müller, Josef Vogel (ČVUT FIT) Mělká a hluboká kopie BI-PA2, 2011, Přednáška 5 25/28 Pole::Pole(int pp) { ptr = new CRPole(pp); Pole::Pole(const Pole& p) { pripoj(p.ptr); Pole::~Pole() { odpoj(); Pole& Pole::operator=(const Pole& p) { if (this==&p) return *this; odpoj(); pripoj(p.ptr); return *this;

p5\pole5\pole.cpp Počítané reference Karel Müller, Josef Vogel (ČVUT FIT) Mělká a hluboká kopie BI-PA2, 2011, Přednáška 5 26/28 void Pole::pripoj(Pole::CRPole* p) { ptr = p; ++p->citacref; void Pole::odpoj() { if (--ptr->citacref == 0) delete ptr;

p5\pole5\main.cpp Počítané reference Karel Müller, Josef Vogel (ČVUT FIT) Mělká a hluboká kopie BI-PA2, 2011, Přednáška 5 27/28 int main() { Pole a(5); for (int i=0; i<a.delka(); i++) a[i] = i; cout << "pole a: " << a << endl; // [0 1 2 3 4] Pole b = a; b[1] = 10; cout << "pole a: " << a << endl; // [0 10 2 3 4] cout << "pole b: " << b << endl; // [0 10 2 3 4] system("pause"); return 0;

Zákaz kopírujícího konstruktoru a přiřazení Pokud pro instance třídy není třeba kopírující konstruktor a přiřazení, lze jejich použití zakázat Jak? Kopírující konstruktor a operátor přiřazení deklarujeme jako private (pak je nemusíme definovat) Karel Müller, Josef Vogel (ČVUT FIT) Mělká a hluboká kopie BI-PA2, 2011, Přednáška 5 28/28