Added article about C++ move. Some fixes.
							parent
							
								
									4cabd6bf4f
								
							
						
					
					
						commit
						65033357d0
					
				@ -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;
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
					Loading…
					
					
				
		Reference in New Issue