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

question-closed Problem z uniwersalnym typem danych dla wszystkich metod wirtualnych klas pochodnych

Object Storage Arubacloud
0 głosów
124 wizyt
pytanie zadane 15 lipca 2018 w C i C++ przez Jakub 0 Pasjonat (23,120 p.)
zamknięte 16 lipca 2018 przez Jakub 0

--- Pytanie jest moim zdaniem dość obszerne, w razie nie zrozumienia go do końca lub pogubienia się w nim będę wdzięczny za danie znać ---

Witam, to już któreś z wielu pytań jakie zadałem na tym forum odnośnie pierwszego projektu jaki piszę w SFML. Struktura całego programu oparta jest na wzorcu projektowym state. Mam klasę abstrakcyjną mającą reprezentować różne stany gry:

-> TheAbstractState //abstrakcyjna klasa bazowa

dziedziczą z niej jak na razie klasy:

-> GameState //sama gra

-> GameOverState //gdy przegramy

-> MenuState //logiczne

Mam główną pętle gry w klasie w której jest wskaźnik na klasę abstrakcyjną wszystkich stanów, w zależności od tego jaki komunikat metody loop() z klas pochodnych klasy theAbstractState zwracają odpowiednio zmieniam stan gry ( wskaźnik ):

void Program::run() {

	while (m_window.isOpen()) {

		StatesReturns sig = m_states->loop(m_window, m_globalTimer);

		if (sig.signals == Signals::EXIT) {
			delete m_states;
			m_window.close(); 
		}

		else if (sig.signals == Signals::GAME) {
			m_globalTimer.restart();
			delete m_states; 
			m_states = new GameState(sf::Vector2u(m_window.getSize().x, m_window.getSize().y));
		}

		else if (sig.signals == Signals::MENU) {
			m_globalTimer.restart();
			delete m_states;
			m_states = new MenuState(); 
		}

		else if (sig.signals == Signals::GAMEOVER) {
			m_globalTimer.restart();
			delete m_states;
			m_states = new GameOverState(sf::Vector2u(m_window.getSize().x, m_window.getSize().y), sig.additionalValue);
		}

		else if (sig.signals == Signals::NONE) {
			delete m_states; 
			m_window.close(); 
		}
	}
}

Signals to odpowiedni typ wyliczeniowy, jednak my zwracamy strukturę:

struct StatesReturns {
	Signals signals;
	int additionalValue;
};

Druga składowa to dodatkowa informacja zwracana przez pętle danego stanu. Np. czas gry w sekundach. Problem jest taki że niektóre z metod loop() coś takiego muszą zwrócić a inne nie. Tam gdzie nie muszą to po prostu zwracałem zero:

if (event.type == sf::Event::Closed) {
	return StatesReturns{ Signals::EXIT,0 };
}

Czasami jednak była potrzeba coś zwrócić dodatkowego:

if (areObjectsDefinitelyEqual(m_player, i))
				return StatesReturns {Signals::GAMEOVER, int(timer.getElapsedTime().asSeconds() - gameStart)};

Wiem że to rozwiązanie nie jest do końca ładne ale przechodziło, wcześniej... teraz pasuje mi żeby metoda loop() z klasy GameState zwróciła jeszcze kolory obiektów gry podczas skucia gracza. W jednym miejscu zwracał bym masę wartości a w innych:

( Signals::jakisSygnal, NULL, NULL )

To nie jest chyba zbyt fajne :/ A nie mogę zmienić typu zwracanego przez metodę wirtualną z klasy bazowej w klasie pochodnej. Myślałem żeby te dodatkowe informacje zwracać za pomocą dodatkowych metod, jednak nie będę mógł je wywołać z poziomu wskaźnika na klasę bazową ( bo dziedziczenie działa tylko w jedną stronę ), zrobił bym jakieś zmienne globalne dla całego projektu ale słyszałem że potrzeba ich zastosowania znaczy że wszystko się napisał źle i kod jest do wyrzucenia ( a ja trochę się napracowałem )

Co powinienem zrobić ;(

komentarz zamknięcia: temat wyczerpany

3 odpowiedzi

+1 głos
odpowiedź 15 lipca 2018 przez profesorek96 Szeryf (91,420 p.)
Przecie C++ udostępnia paradygmat programowania uogólnionego, są tak zwane klasy szablonowe.
komentarz 15 lipca 2018 przez Jakub 0 Pasjonat (23,120 p.)
Nawet na to nie wpadłem... jak na razie umiem tylko szablony funkcji a nie całych klas i struktur, ale pewnie nie jest to jakaś straszna filozofia. Poczytam i zastanowię się na tym rozwiązaniem, dzięki za odpowiedź ;)
komentarz 15 lipca 2018 przez profesorek96 Szeryf (91,420 p.)
Nie jest to żanda filozofia jak umiesz szablony funkcji.
+1 głos
odpowiedź 15 lipca 2018 przez j23 Mędrzec (194,920 p.)

W C++17 masz klasę std::any.

 

Z drugiej strony  nie widzę problemu w tym, żeby do StatesReturns dodać pola, które będą użyte w zależności od wartości signals. To dość powszechne rozwiązanie.

 

Dlaczego nie używasz std::unique_ptr (i std::make_unique)?

1
komentarz 15 lipca 2018 przez Hiskiel Pasjonat (22,830 p.)
Czyli zachowanie w którym mamy np. klasę A, po której dziedziczą B, C i D. Klasa A ma pola int a, int b, int c, int d. I każde z podklas używa tylko swoich intów jest poprawne?
komentarz 15 lipca 2018 przez j23 Mędrzec (194,920 p.)

A kto tu mówi o dziedziczeniu? Mi bardziej chodziło o rozwiązanie w stylu C (jeśli pominąć opcję z std::any).

komentarz 15 lipca 2018 przez Jakub 0 Pasjonat (23,120 p.)

@j23,
 Jeżeli robienie tego tak jak robiłem wcześniej nie jest jakoś strasznie złym rozwiązaniem to może to zastawie :) Zadałem to pytanie bo nie chce zrobić wielu błędów strukturalnych  w kodzie jak i chce zrobić ten projekt porządnie, nie tak jak zawsze gdzie już prawie kompletnie nic nie rozumiem z kodu tych moich starych programów i się nimi brzydzę...

std::any to jak powiedziałeś c++17 a ja na razie dopiero jestem w połowie książki szkoła programowania a sfml się uczę żeby wykorzystać w praktyce poznane niedawno dziedziczenie i zastosować też polimorfizm, jak dojdę do końca to zajmę się nauką nowszych standardów, na razie może to chyba być dla mnie za duży skok naprzód.

Co do stosowania tych inteligentnych wskaźników to doję w książce za dwa rozdziały, na razie chyba nie ma ich potrzeby u mnie stosować bo nie wiedzę miejsca gdzie była by możliwość zapomnienia o zwolnieniu pamięci ( nie mam tu jakoś dużo przydziałów, występują one tylko  w metodzie run() )

+1 głos
odpowiedź 15 lipca 2018 przez monika90 Pasjonat (22,940 p.)
Stan gry może tworzyć następny stan gry. Funkcja loop, gdy się kończy, ustala jaki ma być następny stan gry, tworzy go przekazując do konstruktora wszystkie niezbędne argumenty i zwraca wskaźnik do nowego stanu. Wtedy StatusReturns i Signals nie będzie w ogóle potrzebne. Jak chcesz przykład to powiedz.
komentarz 15 lipca 2018 przez Jakub 0 Pasjonat (23,120 p.)
Cóż, idee całą rozumiem. Tak samo myślę że nie miał bym problemów z implementacją tego sposobu. Musiał bym się na spokojnie zastanowić czy to nie naruszyło by całkiem wszystkich "dziedzin/rejonów" programu, ale raczej tyczyło by się tylko metod loop()... i run()

Zastanowię się nad tym bo to może być niezła myśl, dzięki za radę :)

Podobne pytania

+1 głos
0 odpowiedzi 182 wizyt
pytanie zadane 3 sierpnia 2018 w JavaScript przez szustka124 Gaduła (4,380 p.)
0 głosów
1 odpowiedź 469 wizyt
pytanie zadane 15 listopada 2015 w C i C++ przez Baakoma Użytkownik (780 p.)
0 głosów
1 odpowiedź 446 wizyt
pytanie zadane 31 października 2022 w C i C++ przez Krzysztofs1234 Użytkownik (890 p.)

92,539 zapytań

141,382 odpowiedzi

319,476 komentarzy

61,928 pasjonatów

Motyw:

Akcja Pajacyk

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

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...