Last active
January 22, 2019 14:53
-
-
Save barthap/139f48c182d3af5b6d57b6e35e20171f to your computer and use it in GitHub Desktop.
(PL) Modern C++: Move semantic and dangling reference example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <utility> | |
#include <memory> | |
#include <iostream> | |
#include <vector> | |
#include <stdexcept> | |
class Obiekt | |
{ | |
std::unique_ptr<std::string> nazwa; //celowo unique_ptr a nie zwykły string | |
//aby zademonstrować działanie | |
std::vector<std::reference_wrapper<Obiekt>> relacje; | |
public: | |
explicit Obiekt(std::string&& nazwa) : nazwa(std::make_unique<std::string>(nazwa)) {} | |
Obiekt(const Obiekt&) = delete; //nie chcemy konstruktora kopiującego | |
Obiekt& operator=(const Obiekt&) = delete; //ani operatora przypisania | |
//Obiekt(Obiekt&&) = default; //domyślny konstruktor przenoszący | |
//przeniesie on nam automatycznie unique_ptr wywołując std::move() w tle | |
//Jeśli nie zaimplementujemy własnego konstruktora przenoszącego | |
//Albo użyjemy `= default` j.w. | |
//to kompilator automatycznie wygeneruje prawdopodobnie taki: | |
Obiekt(Obiekt&& src) noexcept | |
{ | |
std::cout << "Konstruktor przenoszący obiektu " << *src.nazwa << std::endl; | |
this->nazwa = std::move(src.nazwa); // <-- !!! | |
this->relacje = std::move(src.relacje); | |
} | |
const std::string& przedstawSie() const { return *nazwa; } | |
void dodajRelacje(Obiekt& rel) | |
{ | |
relacje.push_back(std::ref(rel)); | |
} | |
void wyswietlRelacje() | |
{ | |
std::cout << "Relacje obiektu " << przedstawSie() << ":\n"; | |
for(const auto& rel : relacje) { | |
if(!rel.get().nazwa) | |
throw std::runtime_error("nazwa jest NULLem!!!"); | |
std::cout << " - " << rel.get().przedstawSie() << "\n"; | |
} | |
} | |
}; | |
int main() { | |
std::cout << "Tworze obiekty" << std::endl; | |
Obiekt ob1("ob1"); | |
Obiekt ob2("ob2"); | |
Obiekt ob3("ob3"); | |
std::cout << "Dodaje relacje" << std::endl; | |
ob1.dodajRelacje(ob2); | |
ob1.dodajRelacje(ob3); | |
ob1.wyswietlRelacje(); | |
std::cout << "\nteraz dla wektora:" << std::endl; | |
#define WERSJA 1 //zmień na 1, 2, 3 w zależności od tego którą wersję chcesz uruchomić | |
#if WERSJA == 1 | |
/* | |
* WERSJA 1 | |
* Powoduje wiszące referencje | |
* bo referencje obiektu ob1 (później obiekty[0]) | |
* cały czas wskazują na "oryginalne" ob2 i ob3 | |
* a nie te "przeniesione" do wektora z użyciem std::move() | |
* | |
* A podczas przenoszenia move constructor klasy obiekt | |
* przeniesie unique_ptr z oryginalnego ob1-3 do wektora obiekty[0-2] | |
* przez co w oryginalnych ob1-3 zostaną NULLE | |
* bo unique_ptr ma TYLKO JEDNEGO właściciela | |
* którym podczas przeniesienia zostaje ten obiekt wewnątrz wektora | |
* | |
* Więc przy wywołaniu wyswietlReferencje() ob1 będzie posiadał | |
* dalej wektor referencji wskazujący na oryginalne ob2, ob3 które | |
* już stały się nullem na rzecz wektorowych | |
*/ | |
{ | |
std::vector<Obiekt> obiekty; | |
obiekty.push_back(std::move(ob1)); | |
obiekty.push_back(std::move(ob2)); | |
obiekty.push_back(std::move(ob3)); | |
try { | |
obiekty[0].wyswietlRelacje(); | |
} catch (std::exception &ex) { | |
std::cout << "Rzucono wyjątek: " << ex.what() << std::endl; | |
} | |
} | |
#elif WERSJA == 2 | |
/* | |
* WERSJA 2 | |
* Zamiast move, wektor przechowuje wskaźniki na "oryginalne" obiekty | |
* przez co referencje są ważne cały czas i błędu nie ma | |
*/ | |
{ | |
std::vector<Obiekt*> obiekty = {&ob1, &ob2, &ob3}; | |
obiekty[0]->wyswietlRelacje(); | |
} | |
#elif WERSJA == 3 | |
/* | |
* WERSJA 3 | |
* Tak samo jak wersja 2, z tym że zamiast "gołych" wskaźników, | |
* trzymamy referencje na oryginalne ob1-3 | |
* A że std::vector<Obiekt&> jest nieprawidłowy, należy użyć | |
* specjalnego szablonu std::reference_wrapper | |
*/ | |
{ | |
std::vector<std::reference_wrapper<Obiekt>> obiekty = {std::ref(ob1), std::ref(ob2), std::ref(ob3)}; | |
obiekty[0].get().wyswietlRelacje(); | |
} | |
#endif | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment