• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

question-closed Kopiowanie głębokie obiektu ( wykonywanie kopii inteligentnego wslaźnika std::unique_ptr )

0 głosów
104 wizyt
pytanie zadane 31 marca w C i C++ przez Jakub 0 Stary wyjadacz (13,320 p.)
zamknięte 3 kwietnia przez Jakub 0

Witam, początkowo miałem taki kod:

//-----------------------------------------------------------------------------------------------

	sf::Vector2f windowSize = window.mapPixelToCoords(sf::Vector2i(window.getSize().x, window.getSize().y));

	int numberOfEntities = 80;
	sf::Vector2f entityGenerationPoint = windowSize / 2.f; 
	float entitySeparationDistance = 30.f;
	float entitiesSpeed = 100.f;
	float entitiesNeighborhoodZone = 300.f;
	float timeToStartFlocking = 1.f;

	std::list<Entity> mainFlockingEntityList;
	for (int i = 0; i < numberOfEntities; ++i) {
		mainFlockingEntityList.emplace_back(std::make_unique<ClassicFishShape>(
			ClassicFishShape(sf::Vector2f(20.f, 30.f), std::string("fish.png"), SMALL_FISH)), AdditionalMatch::getRandomFloatValue(1.f, 360.f),
			entitySeparationDistance, i, entityGenerationPoint);
		mainFlockingEntityList.back().setObstacleBehavioursInfo(FlockingObstacle(100.f, 100.f, mainFlockingEntityList.back().getPosition()));
		mainFlockingEntityList.back().setMagnetBehavioursInfo(FlockingMagnet(100.f, 30.f, mainFlockingEntityList.back().getPosition()));
	}

auto mainFlock = std::make_unique<Flock>(mainFlockingEntityList, entitiesSpeed, entitiesNeighborhoodZone, windowSize, timeToStartFlocking, FlockingAlgorithm::FlockingBehaviourParameters{});

Wiem że tego jest dużo, myślę że nawet nie ma sensu objaśniać co ja tu robię ( po prostu tworzę obiekt o nazwie mainFlock ).Tak naprawdę znaczenie ma tylko to wywołanie:

	mainFlockingEntityList.emplace_back(std::make_unique<ClassicFishShape>( ClassicFishShape(sf::Vector2f(20.f, 30.f), std::string("fish.png"), SMALL_FISH)), AdditionalMatch::getRandomFloatValue(1.f, 360.f), entitySeparationDistance, i, entityGenerationPoint);

Korzystam w tym momencie z kontenera std::list i wywołuje metodę emplace_back, tak wygląda konstruktor klasy dodawanego do listy obiektu:

Entity::Entity(std::unique_ptr<EntityShape> entityShape, float angle, float separationSquareRootZone, int ID, sf::Vector2f position)
		: m_SeparationCubedZone(std::pow(separationSquareRootZone, 2)), m_ID(ID), m_eventStaff(nullptr) {

		angle *= AdditionalMatch::DEGREES_TO_RADIANS;
		m_velocity = sf::Vector2f(std::cos(angle), std::sin(angle));
		m_entityShape = std::move(entityShape);
		m_entityShape->setPosition(position);
	}

konstruktor kopiujący jest tu usunięty:

Entity(const Entity& entity) = delete;

Myślę że ważna jest ta linijka:

m_entityShape = std::move(entityShape);

pamięć przydzielam w wywołaniu konstruktora a tu tylko przenoszę wskaźnik.

Wszystko tu działa bez zarzutów. Koszmar się zaczął kiedy zamieniłem wszędzie kontener std::list na std::vector. Pojawił się błąd:

Z tego "ciągu znaków" zrozumiałem tyle że brakuje mi konstruktora kopiującego, nie mam pojęcia czemu wywołanie emplace_back dla std::list go nie wymaga a dla std::vector już tak, ale mi zostało tylko się podporządkować do wymagań kontenerów STL... 

Więc dodałem konstruktor kopiujący:

Entity::Entity(Entity& entity)
		: m_SeparationCubedZone(entity.m_SeparationCubedZone), m_ID(entity.m_ID), m_eventStaff(nullptr){
		m_velocity = entity.m_velocity;
                m_entityShape = std::make_unique<???>(*entity.m_entityShape); 
              
	}

Pewnie pytacie się czemu w miejsce nawiasów ostrych dałem pytajniki, to jest właśnie główny mój problem. Nie wiem co mam tu dać.

Jak zerkniemy do zwykłego konstruktora to widać:

std::unique_ptr<EntityShape> entityShape

EntityShape to klasa bazowa, jak widać na początku, może ona wskazywać na dowolną klasę pochodną:

mainFlockingEntityList.emplace_back(std::make_unique<ClassicFishShape>(...),...); 

podczas wywołania konstruktora podaję jaki typ klasy pochodnej chcę stworzyć.

Wracając do tego momentu:

m_entityShape = std::make_unique<???>(*entity.m_entityShape); 

skąd ja mam wiedzieć na jaki typ klasy pochodnej wcześniej wskazywał entity.m_entityShape? W nawiasach muszę podać typ żeby dokonać kopii, a ja go nie znam ( wiem tylko tyle że sam wskaźnik jest typu klasy bazowej... ). Próbowałem jakoś to tak rozegrać:

m_entity = std::make_unique<decltype(entity.m_entity)>(*entity.m_entity);

Ale po pierwsze nie wydaje mi się to poprawnym rozwiązaniem, a po drugie powoduje to masę błędów. Nie mam zielonego pojęcia jak mam to zrobić :(

Z góry bardzo dziękuję, jeśli czegoś nie rozumiecie a chcecie pomóc to pytajcie ;)

Tu jest cały kod: https://github.com/Jakub1010/Flocking-Algorithm-Project

tyle że wcześniejszy, ten na listach co jeszcze działa poprawnie. Najważniejsze są tam pliki Flocking.h, Flocking.cpp i main.cpp.

 

komentarz zamknięcia: problem rozwiązany.
1
komentarz 31 marca przez mokrowski VIP (111,080 p.)

BTW.

https://pl.wikipedia.org/wiki/Prawo_Demeter

np.

mainFlockingEntityList.back().setMagnetBehavioursInfo(FlockingMagnet(100.f, 30.f, mainFlockingEntityList.back().getPosition())

 

komentarz 1 kwietnia przez Jakub 0 Stary wyjadacz (13,320 p.)
tzn? Mógłbyś rozszerzyć? Z góry dzięki.
1
komentarz 2 kwietnia przez mokrowski VIP (111,080 p.)

Dość proste. Jeśli pytasz obiekt w funkcji o obiekt który zwraca a na nim wykonujesz operację która zwraca inny obiekt, który posiada metodę która zwraca ...  to masz takie "zapuszczenie korzeni" w aplikacji że będzie Ci to bardzo niewygodnie rozplątywać.

Nieco humorystycznie:

if(ciocia.zapytajWójka("jak załatwić pompkę").załatwZzamianąNa(staryRower).pompuj(kołoWózka).zmierzSiłę() < 20Kg) {
    //... 
}

Zaczynasz od Ciotki a kończysz na kilogramach posiadając zbędną wiedzę co się dzieje po drodze. Zbędną z punktu widzenia kodu w którym teraz jesteś.

Poza tym konieczność kopiowania std::unique_ptr świadczyć może o nieadekwatnym wyborze środków (tu wskaźnika) do zadania.

komentarz 3 kwietnia przez Jakub 0 Stary wyjadacz (13,320 p.)

Dziękuje za objaśnienie :) teraz zamieniłem zapis:

mainFlockingEntityList.back().setMagnetBehavioursInfo(FlockingMagnet(100.f, 30.f, mainFlockingEntityList.back().getPosition())

na:
 

mainFlockingEntityList.back().setObstacleBehavioursInfo(100.f,100.f);
mainFlockingEntityList.back().setMagnetBehavioursInfo(100.f,30.f);

Teraz odpowiednia metoda sama bierze odpowiedzialność za ustawianie tego.

1 odpowiedź

0 głosów
odpowiedź 31 marca przez j23 VIP (105,900 p.)
wybrane 31 marca przez Jakub 0
 
Najlepsza

Dodaj do EntityShape wirtualną metodę tworzącą kopie obiektu (zaimplementowaną oczywiście po stronie klas pochodnych).

komentarz 4 kwietnia przez Jakub 0 Stary wyjadacz (13,320 p.)

Nie wiem, po co wywołujesz setPosition na obiekcie, który jest przenoszony.

Sam do końca nie wiem, bez tego pozycja sama się nie ustawi. Wydaje mi się że wynika to z tego że position jest składową klasy tego obiektu:

sf::RectangleShape m_fish;

Jest to już samo "dno abstrakcji" a więc klasa biblioteki SFML. Widocznie w konstruktorze kopiującym tej klasy nie zaimplementowano kopiowania pozycji obiektu. Innej opcji raczej nie ma...

Generalnie to uaktualniłem pytanie/komentarz bo już sam zdałem sobie sprawę z tego co zawaliłem. Jak byś mógł jeszcze raz zerknąć ;) Z góry dziękuje wink

komentarz 4 kwietnia przez j23 VIP (105,900 p.)

Sam do końca nie wiem, bez tego pozycja sama się nie ustawi.

Jak nie ustawi, skoro przenosisz na własność wskaźnik na ten obiekt do innej klasy?

komentarz 4 kwietnia przez j23 VIP (105,900 p.)
mainFlockingEntityList.push_back(Entity(....));

Użyj emplace_back.

Flock::Flock(std::vector<Entity>& entities, ...)
: ..., m_entities(std::move(entities)),

entities powinieneś zrobić r-referencją (teraz trochę to bałamutne).

 

komentarz 4 kwietnia przez Jakub 0 Stary wyjadacz (13,320 p.)

W sumie to problem był w tym że kopiowania pozycji nie dodałem od konstruktora kopiującego klasy EntityShape:

ClassicFishShape(const ClassicFishShape& obj)
		: ClassicFishShape(obj.m_fish.getSize(), obj.m_textureLocation, obj.m_flockingObjectType) {
		m_fish.setPosition(obj.getPosition());
	}

Teraz już to załatwiłem w tamtym miejscu i w tych konstruktorach mogę to pominąć, co nie zmienia faktu że obiekt typu sf::RectangleShape sam tego za mnie nie zrobi.

Najgorsze jest to że kiedy jeden z konstruktorów ( tych trzech klasy Entity ) celowo zepsułem ( nie kopiowałem obiektu m_entityShape ) to było tylko o część obiektów ( tych ryb :) ) mniej. Tak jakby za każdym razem rzeczywiście był wykorzystywany losowo inny konstruktor...

komentarz 4 kwietnia przez Jakub 0 Stary wyjadacz (13,320 p.)

@j23

Początkowo miałem emplace_back, widocznie zastąpiłem to push_back'iem poszukując błędu i zapomniałem zmienić z powrotem.

Tak dla informacji, teraz wywołuje mi się dla Entity tylko konstruktor zwykły i kopiujący ( bez przenoszącego ). W sumie to nwm do końca dlaczego.

Musiałem też zamienić dodatkowo:

auto mainFlock = std::make_unique<Flock>(mainFlockingEntityList, entitiesSpeed, entitiesNeighborhoodZone, displayAreaSize, timeToStartFlocking, FlockingAlgorithm::FlockingBehaviourParameters{});

na:

auto mainFlock = std::make_unique<Flock>(std::move(mainFlockingEntityList), entitiesSpeed, entitiesNeighborhoodZone, displayAreaSize, timeToStartFlocking, FlockingAlgorithm::FlockingBehaviourParameters{});

Ale to raczej oczywiste.

Podobne pytania

0 głosów
1 odpowiedź 147 wizyt
pytanie zadane 30 września 2017 w Grafika i multimedia przez DODO Bywalec (2,960 p.)
0 głosów
0 odpowiedzi 109 wizyt
0 głosów
1 odpowiedź 84 wizyt
pytanie zadane 27 maja 2016 w Sieci komputerowe, internet przez Wiiiciu Obywatel (1,000 p.)
Porady nie od parady
Możesz ukryć, zamknąć lub zmodyfikować swoje pytanie, za pomocą przycisków znajdujących się pod nim. Nie krępuj się poprawić pochopnie opublikowanego pytania czy zamknąć go po uzyskaniu satysfakcjonującej odpowiedzi. Umożliwi to zachowanie porządku na forum.Przyciski pytania

65,753 zapytań

112,388 odpowiedzi

237,306 komentarzy

46,699 pasjonatów

Przeglądających: 237
Pasjonatów: 13 Gości: 224

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto dwie polecane książki warte uwagi. Pełną listę znajdziesz tutaj.

...