Warning
This page is located in archive.

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

courses:b6b36pjc:cviceni:cviceni_6 [2017/09/25 17:13] (current)
richta created
Line 1: Line 1:
 +<​HTML>​
 +<​style>​
 +.code .kw1, .code .kw2, .code .kw4 { color: #00f; font-weight:​ bold; }
 +.code .br0, .code .kw3, .code .me1, .code .me2, .code .nu0 { color: #000; }
 +.code .co1, .code .coMULTI { color: #080; font-style: normal; }
 +.code .co2 { color: #888; font-style: normal; }
 +.code .st0 { color: #a31515; }
 +</​style>​
 +</​HTML>​
  
 +===== Typový systém =====
 +
 +Zopakujme si, jaké typy proměnných a parametrů se mohou vyskytovat v jazyce C%%++%%.
 +
 +^ Typ          ^ Název ​             ^ Příklad přiřazení ​      ^ Příklad parametru funkce ​           ^ Může ''​b''​ být \\ ''​const T''?​ ^ Změna ''​a''​ \\ změní ''​b''?​ ^ Změna ''​*a''​ \\ změní ''​b''?​ ^
 +| ''​T'' ​       | hodnota ​           | ''​T a = b;'' ​           | ''​void f(T a); f(b);'' ​             | ano                            |                             ​| ​                             |
 +| ''​T*'' ​      | ukazatel ​          | ''​T* a = &​b;'' ​         | ''​void f(T* a); f(&​b);'' ​           |                                |                             | ano                          |
 +| ''​const T*''​ | ukazatel na konst. | ''​const T* a = &​b;'' ​   | ''​void f(const T* a); f(&​b);'' ​     | ano                            |                             ​| ​                             |
 +| ''​T&'' ​      | reference ​         | ''​T&​ a = b;'' ​          | ''​void f(T& a); f(b);'' ​            ​| ​                               | ano                         ​| ​                             |
 +| ''​const T&''​ | konst. reference ​  | ''​const T& a = b;'' ​    | ''​void f(const T& a); f(b);'' ​      | ano                            |                             ​| ​                             |
 +
 +===== Kopírování =====
 +
 +Kopírování je nejběžnější operace v C%%++%%. Ke kopírování dochází vždy, když použijeme operátor přiřazení ''​='',​ ale také v jiných situacích; třeba tehdy, když vytváříme nový objekt z jiného.
 +
 +Pojďme se nejdříve zaměřit na případ, že vytváříme nový objekt z jiného, již existujícího objektu. Vzniklý objekt nazýváme **kopie**.
 +
 +<code cpp>
 +void foo(int c) {
 +    // ...
 +}
 +
 +int main() {
 +    int i = 13;
 +
 +    int a = i;  // a je kopie i
 +    int b(i);   // b je kopie i
 +    foo(i); ​    // c je kopie i
 +}
 +</​code>​
 +
 +Funkce, která zajíšťuje tvorbu kopií, se nazývá **kopírující konstruktor**. Podobně jako základní konstruktor i základní konstruktor je automaticky vytvořen kompilátorem,​ pokud nenapíšeme vlastní. Díky tomu je možné vytvářet kopie vlastních typů, aniž bychom kopírující konstruktor museli psát:
 +
 +<code cpp>
 +struct MyStruct {
 +    int a;
 +    double b;
 +};
 +
 +void foo(MyStruct c) {
 +    // ...
 +}
 +
 +int main() {
 +    MyStruct i = { 11, 2.9 };
 +
 +    MyStruct a = i; // a je kopie i
 +    MyStruct b(i);  // b je kopie i
 +    foo(i); ​        // c je kopie i
 +}
 +</​code>​
 +
 +U některých tříd je kompilátorem generovaný kopírující konstruktor nežádoucí.
 +
 +   * Stáhněte si soubor: {{:​courses:​a7b36pjc:​cviceni:​cviceni_6_copy_fail.zip|Zip}} [[http://​coliru.stacked-crooked.com/​a/​14e2176a8faf45da|Coliru]]
 +   * Zkompilujte program a spusťte ho. Co se stane?
 +   * Pokuste se chování programu vysvětlit.
 +   * Napište vlastní kopírující konstruktor pro třídu ''​vector''​. Kopírující konstruktor bere jako parametr ''​const vector&''​.
 +
 +Někdy bychom rádi, aby se naše objekty kopírovat nedaly. K tomu slouží ''​= delete''​ v deklaraci kopírujícího konstruktoru:​
 +
 +<code cpp>
 +class vector {
 +public:
 +    ...
 +    vector(const vector& rhs) = delete;
 +}
 +</​code>​
 +
 +Příkladem objektu ze standardní knihovny, který nelze kopírovat, je ''​std::​unique_ptr''​. Pokud máme ''​unique_ptr''​ v naší třídě, kopírující konstruktor se automaticky nevytvoří.
 +
 +  * Stáhněte si ''​vector''​ s ''​unique_ptr''​ z minulého cvičení: {{:​courses:​a7b36pjc:​cviceni:​cviceni_5_unique_ptr.zip|}}
 +  * Pokuste se vytvořit kopii ''​vector''​u,​ třeba pomocí ''​vector v2 = v;''​. Co se stane?
 +  * Vytvořte kopírující konstruktor pro třídu ''​vector''​.
 +
 +===== Přesun =====
 +
 +Přesun je podobný kopii, ale umožňuje změnit objekt, ze kterého kopírujeme. Přesun nastává v těchto situacích:
 +
 +  * Při návratu z funkce pomocí ''​return''​.
 +  * Pokud si to vyžádáme pomocí ''​std::​move()''​.
 +
 +Příklady přesunů:
 +
 +<code cpp>
 +#include <​vector>​
 +#include <​iostream>​
 +
 +void print_vector(const std::​vector<​int>&​ v) {
 +    for (auto& item : v) std::cout << item << ' ';
 +    std::cout << '​\n';​
 +}
 +
 +std::​vector<​int>​ get() {
 +    std::​vector<​int>​ v = { 1, 23, 4, 1 };
 +    return v;
 +}
 +
 +int main() {
 +    std::​vector<​int>​ v1 = get(); ​          // v přesunut do v1
 +    std::​vector<​int>​ v2 = std::​move(v1); ​  // v1 přesunut do v2
 +    std::cout << "v1: "; print_vector(v1);​ // v1:
 +    std::cout << "v2: "; print_vector(v2);​ // v2: 1 23 4 1
 +}
 +</​code>​
 +
 +Účelem přesunu je zefektivnit některé operace. V předchozím kódu bychom například mohli vědět, že ''​v1''​ už nebudeme potřebovat,​ proto jsme ho do ''​v2''​ přesunuli. Pokud vracíme hodnotu ve funkci, tak není pochyb, že původní objekt dál už nepotřebujeme.
 +
 +Funkce ''​std::​move''​ má speciální návratový typ zvaný //rvalue reference//​. Rvalue reference se píše ''​T&&''​ a značí referenci na objekt, který má vzápětí zaniknout. Díky tomu můžeme odlišit další druh konstruktoru,​ tzv. **přesunující konstruktor**.
 +
 +  * Přidejte do třídy ''​vector''​ přesunující konstruktor,​ jeho parametr bude typu ''​vector&&''​. V těle konstruktoru si přivlastněte pole původního objektu a původní objekt nastavte na prázdný.
 +  * Přidejte do všech konstruktorů a destruktoru kontrolní výpisy, které prozradí, kdy se která z těchto funkcí volá.
 +  * Vyzkoušejte upravenou třídu pomocí této funkce ''​main'':​
 +
 +<code cpp>
 +int main() {
 +    vector v1;
 +    v1.push_back(1.23);​
 +    v1.push_back(2.34);​
 +    std::cout << '​\n';​
 +    vector v2 = v1;
 +    std::cout << '​\n';​
 +    vector v3 = std::​move(v2);​
 +    std::cout << '​\n';​
 +
 +    std::cout << "v1: ";
 +    print_vector(v1);​
 +    std::cout << "v2: ";
 +    print_vector(v2);​
 +    std::cout << "v3: ";
 +    print_vector(v3);​
 +}
 +</​code>​
 +
 +Pokud doplníme typový systém o rvalue reference, získáme takovouto tabulku:
 +
 +^ Typ          ^ Název ​             ^ Příklad přiřazení ​        ^ Příklad parametru funkce ​           ^ Může ''​b''​ být \\ ''​const T''?​ ^ Změna ''​a''​ \\ změní ''​b''?​ ^ Změna ''​*a''​ \\ změní ''​b''?​ ^
 +| ''​T'' ​       | hodnota ​           | ''​T a = b;'' ​             | ''​void f(T a); f(b);'' ​             | ano                            |                             ​| ​                             |
 +| ''​T*'' ​      | ukazatel ​          | ''​T* a = &​b;'' ​           | ''​void f(T* a); f(&​b);'' ​           |                                |                             | ano                          |
 +| ''​const T*''​ | ukazatel na konst. | ''​const T* a = &​b;'' ​     | ''​void f(const T* a); f(&​b);'' ​     | ano                            |                             ​| ​                             |
 +| ''​T&'' ​      | reference ​         | ''​T&​ a = b;'' ​            | ''​void f(T& a); f(b);'' ​            ​| ​                               | ano                         ​| ​                             |
 +| ''​const T&''​ | konst. reference ​  | ''​const T& a = b;'' ​      | ''​void f(const T& a); f(b);'' ​      | ano                            |                             ​| ​                             |
 +| ''​T&&'' ​     | rvalue reference ​  | ''​T&&​ a = std::​move(b);''​ | ''​void f(T&&​ a); f(std::​move(b));''​ |                                | ano                         ​| ​                             |
 +
 +===== Přiřazení =====
 +
 +Našemu ''​vectoru''​ stále ještě chybí jedna důležitá schopnost, neumí totiž správně zkopírovat hodnotu do //již existujícího//​ objektu. Jinými slovy, tento kód stále skončí katastrofou:​
 +
 +<code cpp>
 +int main() {
 +    vector v1;
 +    vector v2;
 +    v1 = v2;
 +}
 +</​code>​
 +
 +Stali jsme se obětí automaticky generovaného **kopírujícího přiřazení**. To se zavolá v případě, že použijeme operátor ''​=''​ na již existující objekt. I tuto speciální funkci si budeme muset naimplementovat sami. Uděláme to tak, že přetížíme operátor ''​=''​.
 +
 +  * Přidejte do třídy ''​vector''​ kopírující přiřazení. Deklarace kopírujícího přiřazení bude vypadat takto: ''​vector&​ operator=(const vector& rhs)''​. Uvnitř funkce zkopírujte obsah původního objektu ''​rhs''​ do tohoto objektu. Přiřazení bude vracet referenci na tento objekt, proto by poslední řádek měl být ''​return *this;''​.
 +  * Co se stane, když ''​rhs''​ je tentýž objekt, jako ''​*this''?​ Pokud se stane něco špatného, funkci opravte.
 +  * Pro úplnost přidejte také **přesunující přiřazení**. Deklarace přesunujícího přiřazení bude vypadat takto: ''​vector&​ operator=(vector&&​)''​.
 +  * Vyzkoušejte upravenou třídu na této funkci ''​main'':​
 +
 +<code cpp>
 +int main() {
 +    vector v1;
 +    v1.push_back(1.23);​
 +    v1.push_back(2.34);​
 +    vector v2;
 +    v2 = v1;
 +    vector v3;
 +    v3 = std::​move(v2);​
 +
 +    std::cout << "v1: ";
 +    print_vector(v1);​
 +    std::cout << "v2: ";
 +    print_vector(v2);​
 +    std::cout << "v3: ";
 +    print_vector(v3);​
 +}
 +</​code>​
 +
 +{{:​courses:​a7b36pjc:​cviceni:​cviceni_6_final.zip|Řešení}}
courses/b6b36pjc/cviceni/cviceni_6.txt · Last modified: 2017/09/25 17:13 by richta