virtual + polimorfizm a przeciążanie funkcji

0

Cześć!

Pisząc grę napotkałem problem. Dziedziczenie wygląda tak : cPostac->cGracz/cWrog
Chcę przekazać wrogowi pozycję gracza, żeby mógł w niego wycelować.
cPostac ma metodę : virtual void aktualizuj(float pobranyCzas);, cGracz nie ma potrzeby przekazywania czegoś innego niż czas więc nic nie nadpisywałem.
metoda aktualizuj aktualizuje następująco : poruszanie, animację i strzelanie. Strzelanie wymaga pozycji gracza więc wygląda to tak:

wrogów mam zapisanych w
std::vector<std::unique_ptr<cPostac>> Wrogowie;

//main.cpp
		for (auto const& i : Wrogowie)
			i->aktualizuj(timer1.getElapsedTime().asSeconds(), Gracz->zwrocPozycje()); 
void cWrog::aktualizuj(float pobranyCzas, sf::Vector2f pozcyjaGracza)
{
    aktualizujAnimacje(pobranyCzas);

	if (oddawanieStrzalu == false)
		aktualizujPolozenie(pobranyCzas);

    aktualizujStrzelanie(pobranyCzas, pozcyjaGracza);
}

->

void cWrog::aktualizujStrzelanie(float pobranyCzas, sf::Vector2f pozycjaGracza)
{
//...
		wycelujStrzelba(pozycjaGracza);
//...
}

->

void cWrog::wycelujStrzelba(sf::Vector2f pozycjaGracza)
{
	//algorytm celowania
}

Wiem, że to może być bez sensu takie przekazywanie ale chcę rozbijać te funkcje na jak najprostsze no i nie chcę w mainie wywoływać każdej funkcji (aktualizuj polozenie, animacje strzelanie) bo nie zawsze aktualizuje je na raz w logice gry.

Korzystając z tego tematu zbudowałem podobny program, który obrazuje moją sytuację:

#include <iostream>
#include <vector>
#include <memory>

struct A {
	virtual void Show() const { std::cout << __FUNCTION__ << '\n'; }
};

struct B : public A {
	virtual void Show(int a) const  { std::cout << __FUNCTION__  << a << '\n'; }
};

struct C : public A {
	virtual void Show(int a, int b) const  { std::cout << __FUNCTION__ << a << b << '\n'; }
};

int main() {
	std::vector<A> objects;
	objects.push_back(B{});
	objects.push_back(C{});

	std::vector<std::unique_ptr<A>> ptrs;
	ptrs.push_back(std::make_unique<B>());
	ptrs.push_back(std::make_unique<C>());

	std::cout << "Kontener objects: \n";
	objects[0].Show();
	objects[1].Show(1);
	objects[2].Show(1, 2);

	std::cout << "Kontener ptrs: \n";
	ptrs[0]->Show();
	ptrs[1]->Show(1);
	ptrs[2]->Show(1, 2);

	std::cin.get();
}

w przekazywaniu Show() intów wywala błąd, bez wartości działa poprawnie. Jak mogę rozwiązać ten problem ?

1
std::vector<A> objects;
objects.push_back(B{});

Czego tutaj się spodziewasz? objects potrafi przechowywać wyłącznie elementy typu A.

0
pingwindyktator napisał(a):
std::vector<A> objects;
objects.push_back(B{});

Czego tutaj się spodziewasz? objects potrafi przechowywać wyłącznie elementy typu A.

No dobra, tutaj to ja rozumiem. Ale ptrs ?

Po co dodawać pośredni sposób odwołania od obiektów skoro istnieje bezpośredni?

5

Jak korzystasz z polimorfizmu, musisz korzystać ze wskaźników, inaczej nie ma to sensu, bo nie ma polimorfizmu.
Polimorfizm i overloading to bardzo nietrafione połączanie. Oczywiście, że się da, ale po co? Powoduje to więcej kłopotów niż zysków.

Kiedyś ktoś mi napisał interface z przeciążonymi metodami. Jak go musiałem zaimplementować to musiałem napisać kod, który niczego nie wnosił (było 5 przeciążeń i tylko jeden miał sensowny kod reszta tylko zaspakajała przeciążenie wywołując tą jedną metodę). I tak za każdym razem kiedy implementowało się interface.
Interface (klasa tylko z metodami czysto witalnymi) zwykle stanowi deficjencję wymagań dla obiektu, ergo im mniejsze wymagania się stawia (mniej metod), tym łatwiej je zaspokoić i łatwiej utrzymać kod.
Mimo, że interface to przypadek szczególny, to IMO pokazuje ogólną zasadę bezsensowności przeciążeń dla metod wirtualnych.

Dlatego uważam, że przeciążanie powinno być wyłącznie dla metod niewirtualnych, a jeszcze lepiej jeśli jest używane jedyni dla klas o naturze struktury (coś czego kopiowanie ma sens), a nie obiektu.

1

Funkcje wirtualne tak nie działają. Nie możesz w klasie dziedziczącej zmieniać parametrów przyjmowanych przez funkcję z klasy bazowej. Moim zdaniem źle podchodzisz do problemu i przekazywanie różnych parametrów w metodzie aktualizuj w ogóle nie jest potrzebne. Zrób sobie klasę Świat, która będzie zawierała informacje o świecie i kontenery z postaciami, potem w każdej klasie Postać daj wskaźnik na *Świat *i niech każda Postać w metodzie void aktualizuj() sobie pobiera ze świata potrzebne jej informacje, takie jak czas, wskaźnik na gracza itd.

1

Albo niech klasa cPostac posiada wskaźnik na cPostac, który będzie określał kogo ma atakować + do tego funkcja UstawCel.

0

Dodałem przeciążenie do aktualizuj(); i nie działało. Zmieniłem typ wskaźnika z cPostac na cWrog i śmiga std::vector<std::unique_ptr<cWrog>> Wrogowie; Na razie niech tak zostanie.
Podoba mi się pomysł z klasą świat. Kiedyś przetestuję :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1