Pozbycie się statycznego obiektu

0

A więc tak: robię grę, która w mainie ma tylko obiekt klasy Gra i wywołuje funkcje Gra.Wyswietl(...);

Klasa Gra w zależności od zmiennej statusGry, włącza kolejne poziomy w taki sposób (w uproszczeniu napisałem, żeby zawrzeć sens):

switch(statusGry)
{     
	case 1:
		WyswietlPoziom<Poziom1>(*Okno, Zdarzenie, kliknieto, posX, posY, statusGry);
		break;
     	case 2:
		WyswietlPoziom<Poziom2>(*Okno, Zdarzenie, kliknieto, posX, posY, statusGry);
		break;
    	case 3:
		WyswietlPoziom<Poziom3>(*Okno, Zdarzenie, kliknieto, posX, posY, statusGry);
		break;
}
 

Natomiast szablon wygląda tak:

template<class T>void Gra::WyswietlPoziom(sf::RenderWindow &Okno, sf::Event &Zdarzenie, int &kliknieto, int &posX, int &posY, int &statusGry)
{	
	static T T;
        //(...)
	T.Wyswietl(Okno, Zdarzenie, kliknieto, posX, posY, statusGry);
}
 

Problem jest taki. Jestem powiedzmy w pierwszym poziomie - gra zużywa 30 mb pamięci. Wchodzę do drugiego poziomu gra zużywa 45mb, trzeci poziom 60mb, itd. Wnioskuję, że gra zużywa jej coraz więcej, bo mimo, że jestem w Poziomie2 to cały czas istnieje statyczny Poziom1, itd.

I tu się pojawia problem? Jak "zwolnić" taką zmienną statyczną zaraz po zmianie statusuGry albo jak zrobić to w inny sposób. Chodzi mi o takie rozwiązanie, żeby dla danej wartości statusGry istniał obiekt tylko jednego poziomu, a nie też tych poprzednich. Wskaźnik do obiektu klasy bazowej, który wskazuje w danej chwili na dany obiekt klasy pochodnej czyli polimorfizm? Czy jak myślicie?

Dziękuje z góry za pomoc.

0

Ale dlaczego obiekt ten jest static? Nie widzę tutaj sensownego powodu

0

Zmienna statyczna już istnieje zanim stworzyłeś pierwszy obiekt klasy i wciąż istnieje po tym jak zwolniłeś ostatni obiekt klasy. Więc problemy z pamięcią nie są związane z tym że zmienna jest typu static.

0

To nie wiem jaki mam problem. Jeśli bym wszedł do 4 poziomu powiedzmy na początku to mam te ~30mb zużycia, a jak dojde do niego po kolei wszystkimi poziomami to będzie ~75mb. Zastanawiam się już jakiś czas czym to może być spowodowane, bo to chyba znaczy, że coś z tych poprzednich poziomów się wciąż wykonuje, nie wiem..

Na początku w ogóle miałem zużycie ~150mb, bo w klasie Gra miałem od razu zadeklarowane wszystkie poziomy, przez co uruchamiały się konstruktory tych poziomów, wszystko istniało przez cały czas trwania programu. Usunięcie każdego poziomu z klasy Gra powodowało zmniejszenie tego zużycia do ok. 30mb. Dopiero jak zacząłem tworzyć obiekty konkretnych poziomów w momencie zmiany statusuGry to program miał zuzycie koło 30mb na starcie. Pojawia się statusGry=1 to tworzę obiekt klasy Poziom1, statusGry=2 tworzę obiekt klasy Poziom2, widać to tam u góry. Chciałbym, żeby program na ostatnim poziomie nie przechowywał wszystkich poprzednich poziomów ,bo to będzie masakra.

A użyłem static, bo ta metoda szablonowa chodzi w pętli i kiedy daje T T, a nie static T T, to są błędy, bo metoda przy każdym przejściu pętli tworzy na nowo ten obiekt. To że dałem static pozwoliło go raz stworzyć, a potem już sobie istnieje po prostu.

1

Zmienna statyczna jest szablonowa, co oznacza, że dla każdej specjalizacji metody tworzony jest nowy obiekt statyczny. Statyczny obiekt ma czas życia od pierwszego napotkania deklaracji aż do zakończenia wywołania programu.

Static średnio nadaje się do twojego przypadku – to tylko (dość brzydkie, muszę przyznać) ominięcie problemu wielokrotnej konstrukcji ze względu na wielokrotne wywołanie metody. O wiele lepszym rozwiązaniem byłoby przekazanie referencji do gotowego obiektu. W zasadzie szablon nie jest koniecznym rozwiązaniem – polimorfizm dynamiczny lepiej się tutaj sprawdzi. Proponowałbym coś takiego:

class ILevel {
public:
    ILevel() {
        // ...
    }
    
    virtual ~ILevel() {
        // ...
    }
    
    virtual void show() = 0;
};

class Level1 : public ILevel {
public:
    Level1() : ILevel() {
        // ...
    }
    
    virtual void show() {
        // ...
    }
};

class Game {
public:
    Game(sf::RenderWindow& wnd) : wnd(wnd), clicked(0), posX(0), posY(0), status(0) {
        // ...
    }
    
    void addLevel(shared_ptr<ILevel> level) {
        levels.push_back(level);
    }
    
    void show(sf::Event& event) {
        // ...
        showLevel(event);
        // ...
    }

    void nextLevel() {
        ++status;
    }
    
private:
    showLevel(sf::Event &event) {
        assert(status >= 0);
        assert(status < levels.size());
        
        ILevel& level = *levels[status];
        
        // ...
        
        level.show(wnd, event, clicked, posX, posY, status);
    }

    // add getters (and setters if needed)
    sf::RenderWindow& wnd;
    int clicked;
    int posX, posY;
    int status;
    vector<shared_ptr<ILevel>> levels;
};

To oczywiście tylko luźny draft, bo nie wiem jak wygląda twoja architektura. W każdym razie: szablony to nie ta droga, dziedziczenie + polimorfizm załatwia to, czego chcesz. I to w o wiele prostszy sposób, bez dziwnego switcha.

0

Noo, idealnie, właśnie o coś takiego mi chodziło ;) Dziękuje bardzo, problemu nie powinno być z przerobieniem mojego kodu, bo wszystkie poziomy dziedziczą po klasie PoziomBazowy, więc będzie można zastosować polimorfizm bez żadnych zmian. Jeszcze raz dzięki :)

0

Okej, wprowadziłem zmiany i teraz wygląda to u mnie tak:

 
class PoziomBazowy
{
protected:	
	//...
	Tekstura Tlo;
	Postac Gracz;
	sf::Clock timer;
        //...
public:
	//...
	virtual void Wyswietl(sf::RenderWindow &Okno, sf::Event &Zdarzenie, int &kliknieto, int &posX, int &posY, int &nowaGra);
       //...
};
//////////////////////////////////////////////////////
class Poziom1: public PoziomBazowy
{
private:	
	//...
	Klucz Klucz;
	Drzwi Drzwi;
	//itd itd..
public:
	void Wyswietl(sf::RenderWindow &Okno, sf::Event &Zdarzenie, int &kliknieto, int &posX, int &posY, int &nowaGra);
        //...
};
//////////////////////////////////////////////////////

Natomiast w klasie Gra mam wskaźnik:

 

PoziomBazowy *Poziom;

// i ustawiam go w takim trochę niechlujny, brzydki sposób, ale na razie jest tak:

        if(nowaGra == 1) Poziom = new Poziom1(); 
	if(nowaGra == 2) Poziom = new Poziom2(); 
	if(nowaGra == 3) Poziom = new Poziom3();
	if(nowaGra == 4) Poziom = new Poziom4();
	if(nowaGra == 5) Poziom = new Poziom5(); 
	if(nowaGra == 6) Poziom = new Poziom6(); 

//i potem wywołanie metody:

Poziom->Wyswietl(*Okno, Zdarzenie, kliknieto, posX, posY, nowaGra);

Jest jeszcze pare innych zmiennych, inne metody, które trzeba wywołać, ale to jest szkielet. powiedzmy. Tylko do końca nie wiem czy takie podejście do polimorfizmu w tym przypadku jest dobre, bo tam wyżej zaproponowany został polimorfizm ale z użyciem tablicy.

Aha i uprzedzam jak coś, nie dałem rady zrobić poziomów wszystkich za pomocą jednej klasy a potem pobierać informacje na temat konkretnych poziomów np. z pliku. Chciałem, ale jako że to pierwsza moja gra to nie udało mi się tego zrobić, a poziomy na prawdę diametralnie się różnia.

No i ciągle mam ten problem co wcześniej, że przy wejściu do każdego kolejnego poziomu zużycie pamięci w grze wzrasta o ok. ~15mb. Tak jakby te poziomy wciąż istniały/coś tam robiły w tej grze. Grze to nie przeszkadza póki co, ale nie podoba mi się to, na pewno tak nie powinno być.

0

Skoro to jest C++, to na litosc Boska korzystaj z tego C++:
http://en.cppreference.com/w/cpp/memory/shared_ptr
http://en.cppreference.com/w/cpp/memory/unique_ptr
http://en.cppreference.com/w/cpp/memory/weak_ptr
zamiast gwiazdek.

i zamiast tej drabinki ifow jakas fabryka/prototyp (drugie pamieciozerne bedzie).

0

Jestem raczej początkującym programistą i nie znałem jeszcze inteligentnych wskaźników, dopiero teraz trochę o nich poczytałem, głównie o shared_ptr. No i szczerze mówiąc nie umiem się niby posługiwać. Mam je z boost, bo nie mój VS2008 nie rozpoznaje biblioteki <memory>.

Stworzyłem sobie zamiast PoziomBazowy *Poziom zmienną boost::shared_ptr <PoziomBazowy> wsk. Ale to jest dobre podejście czy powininem zrobić vectora tych shared_ptr? No i jak potem na nim operować, bo nie wiem w jaki sposób korzystać z tego wskaźnika w moim programie. Pewnie pytanie infantylne, ale serio nie wiem co musze zrobić, żeby ten wskaźnik działał w moim przypadku.

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