From 65033357d032d95a53993d0c23f4ecc0746d7474 Mon Sep 17 00:00:00 2001 From: Josef Rokos Date: Wed, 3 Mar 2021 10:11:53 +0100 Subject: [PATCH] Added article about C++ move. Some fixes. --- content/about.md | 2 +- content/cpp_move.md | 95 ++++++++++++++++++++++++++++ themes/zola.386/templates/index.html | 5 +- 3 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 content/cpp_move.md diff --git a/content/about.md b/content/about.md index ff84575..0033a05 100644 --- a/content/about.md +++ b/content/about.md @@ -2,7 +2,7 @@ title = "O webu" +++ -Jsem programátor v C++, Rustu, Javě a trochu JavaScriptu. Hobby fotograf. Tento web je soubor mojich +Jsem programátor v C++, Rustu, Javě a trochu JavaScriptu. Hobby fotograf. Tento web je soubor mých poznámek. diff --git a/content/cpp_move.md b/content/cpp_move.md new file mode 100644 index 0000000..15db84b --- /dev/null +++ b/content/cpp_move.md @@ -0,0 +1,95 @@ ++++ +title = "C++ move semantika" +description = "Jak funguje move v C++" +date = 2021-03-03 +draft = false +slug = "cpp-move" + +[taxonomies] +categories = ["programování"] +tags = ["C++"] ++++ + +Slouží k přesunu věcí z jednoho objektu do druhého. Originální objekt může po přesunu být v nekonzistentním stavu. + +## Lhodnota a Rhodnota (lvalue, rvalue) + +Definice z C je, že lvalue je výraz, který může být na levé i pravé straně přířazení a rvalue je výraz, který může být pouze na pravé straně přířazení. + +```cpp + int a = 42; + int b = 43; + + // a a b jsou lvalue: + a = b; // ok + b = a; // ok + a = a * b; // ok + + // a * b je rvalue: + int c = a * b; // ok, rvalue na pravé straně přiřazení + a * b = 42; // chyba, rvalue na levé straně přiřazení +``` + +V C++ je to o trošku složitější. Jsou tam malé rozdíly v použití oprátoru `&`, např. v C nelze vrátit z funkce referenci `int& funkce() {}`. Toto lze pouze v C++. Definovat lvalue a rvalue teda můžeme tak, že lvalue je výraz, ze kterého jde udělat reference na paměť pomocí operátoru `&`, rvalue je všechno co není lvalue. + +```cpp + // lvalues: + // + int i = 42; + i = 43; // ok, i je lvalue + int* p = &i; // ok, i je lvalue + int& foo(); + foo() = 42; // ok, foo() je lvalue + int* p1 = &foo(); // ok, foo() je lvalue + + // rvalues: + // + int foobar(); + int j = 0; + j = foobar(); // ok, foobar() je rvalue + int* p2 = &foobar(); // chyba, nelze získat adresu paměti rvalue + j = 42; // ok, 42 je rvalue +``` + +## Move semantika + +Mějme třídu `X`, která v sobě drží raw ukazetel `m_pointer` a délku dat `m_length`. Pokud instanci této třídy budeme chtít přiřadit operátorem `=`, uděláme to nějak takhle: + +```cpp +X& X::operator=(X const & rhs) +{ + // uvolnit původní paměť + free(this->m_pointer); + + // kopie paměti + this->m_length = rhs.m_length; + this->m_pointer = malloc(rhs.m_length); + memcpy(this->m_pointer, rhs.m_pointer, rhs.m_length); +} +``` + +Třídu následně použijeme takto: + +```cpp +X foo(); +X x; +// nějaké použití x +x = foo(); +``` + +Na posledním řádku se stane: +- zkopíruje se paměť `m_pointer` z dočasné instance vrácené z funkce `foo()` +- uvolní se původní paměť držená instancí `x` a nahradí se novou z dočasné instance +- uvolní se dočasná instance + +Toto sice funguje, ale efektivnější je v tomto případě pouze přesunout ukazetel z dočasné instance do instance `x`. Funkce `foo()` ale vrací rvalue, takže nelze získat adresu paměti operátorem `&`. Od C++11 je proto zavedený nový operátor `&&`, kterým lze získat referenci na rvalue, tedy místo v paměti, kde se nachází dočasná instance vrácená z `foo()`. Operátor přiřazení pak můžeme definovat jako: + +```cpp +X& X::operator=(X&& rhs) +{ + this->m_length = rhs.m_length; + this->m_pointer = rhs.m_pointer; + rhs.m_pointer = nullptr; + rhs.m_length = 0; +} +``` \ No newline at end of file diff --git a/themes/zola.386/templates/index.html b/themes/zola.386/templates/index.html index 095bd65..a7f6df4 100644 --- a/themes/zola.386/templates/index.html +++ b/themes/zola.386/templates/index.html @@ -122,7 +122,8 @@

@@ -130,7 +131,7 @@ {% endblock sidebar %}