Added article about C++ move. Some fixes.
This commit is contained in:
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
title = "O webu"
|
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.
|
poznámek.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -122,7 +122,8 @@
|
|||||||
<ul class="nav nav-list bs-docs-sidenav">
|
<ul class="nav nav-list bs-docs-sidenav">
|
||||||
{% set categories = get_taxonomy(kind="categories") %}
|
{% set categories = get_taxonomy(kind="categories") %}
|
||||||
{% for cat in categories.items %}
|
{% for cat in categories.items %}
|
||||||
<li><a href="{{ config.base_url }}/categories/{{ cat.name }}">{{ cat.name }}</a></li>
|
<!--<li><a href="{{ config.base_url }}/categories/{{ cat.name }}">{{ cat.name }}</a></li>-->
|
||||||
|
<li><a href="{{ cat.permalink }}">{{ cat.name }}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
<p></p>
|
<p></p>
|
||||||
@@ -130,7 +131,7 @@
|
|||||||
<ul class="nav nav-list bs-docs-sidenav">
|
<ul class="nav nav-list bs-docs-sidenav">
|
||||||
{% set tags = get_taxonomy(kind="tags") %}
|
{% set tags = get_taxonomy(kind="tags") %}
|
||||||
{% for tag in tags.items %}
|
{% for tag in tags.items %}
|
||||||
<li><a href="{{ config.base_url }}/tags/{{ tag.name }}">{{ tag.name }}</a></li>
|
<li><a href="{{ tag.permalink }}">{{ tag.name }}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endblock sidebar %}
|
{% endblock sidebar %}
|
||||||
|
|||||||
Reference in New Issue
Block a user