Skip to content

Instantly share code, notes, and snippets.

@barthap
Last active January 22, 2019 14:53
Show Gist options
  • Save barthap/139f48c182d3af5b6d57b6e35e20171f to your computer and use it in GitHub Desktop.
Save barthap/139f48c182d3af5b6d57b6e35e20171f to your computer and use it in GitHub Desktop.
(PL) Modern C++: Move semantic and dangling reference example
#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