Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[C++][STL] Polimorfizm i wskaźniki w kontenerach

Ostatnio zmodyfikowano 2015-05-01 20:03
Autor Wiadomość
Kopczak1995
Temat założony przez niniejszego użytkownika
[C++][STL] Polimorfizm i wskaźniki w kontenerach
» 2015-05-01 11:16:59
Witam mam mały problem z biblioteką STL.
W projekcie mojej gry zdecydowałem się użyć polimorfizmu dla obiektów wroga i gracza. Wygląda to pi razy oko tak:
C/C++
//klasa główna
class Tank;

//klasy pochodne
class Enemy;
class Player;

//W takiej liście przechowuję wszystkie obiekty.
std::list < Tank *> tanks;

//Alokuję w ten sposób:
Enemy * nowy = new Enemy;
tanks.puch_back( nowy );

//lub dla gracza:
Player * nowy = new Player;
tanks.puch_back( nowy );
Mam jednak problem z dealokacją...
C/C++
//pozbywanie się zniszczonych czołgów
//każdy czołg przechowuje informację bool exist czy ma być dealokowany = zniszczony
for( auto & tank: tanks ) {
    if( tank->exist == false ) {
        delete tank;
    }
}
tanks.erase( std::remove( tanks.begin(), tanks.end(), nullptr ), tanks.end() );
Wyszedłem z założenia, że to zwykła lista wskaźników i wywalenie czołgu poprzez
delete tank
 dealokuje co trzeba i pozostanie mi pusty wskaźnik na
nullptr
 ale w rzeczywistości siedzą tam jeszcze jakieś śmieci, co sprawia, że ciężko jest dalej pracować, sypie mi errorami :/

Próbowałem używać destruktorów wirtualnych, czytałem że trzeba je wszędzie umieszczać gdy mamy polimorfizm aby
delete
 mogło "zajarzyć" z którym typem mamy do czynienia, jednak dodanie tego wiele nie pomogło :/
P-131669
pekfos
» 2015-05-01 13:27:13
Wyszedłem z założenia, że to zwykła lista wskaźników i wywalenie czołgu poprzez
delete tank
 dealokuje co trzeba i pozostanie mi pusty wskaźnik na
nullptr
 ale w rzeczywistości siedzą tam jeszcze jakieś śmieci, co sprawia, że ciężko jest dalej pracować, sypie mi errorami :/
Więc przypisz nullptr po użyciu delete..? To jakby oczywiste.
P-131671
Kopczak1995
Temat założony przez niniejszego użytkownika
» 2015-05-01 13:56:46
Tak w sumie już zrobiłem, tylko nie do końca rozumiem co się stało po dealokacji.
Powiedz mi czy dobrze myślę - wskaźnik w kontenerze wciąż wskazuje na tamten adres, lecz po użyciu delete nic tam już nie ma?
P-131672
kubawal
» 2015-05-01 14:09:15
puch_back
Hmmm...

C/C++
delete tank
 dealokuje co trzeba i pozostanie mi pusty wskaźnik na
C/C++
nullptr
Może od razu erase'uj ten element z listy?
P-131673
Kopczak1995
Temat założony przez niniejszego użytkownika
» 2015-05-01 15:39:43
Hmmm... Pytanie tylko czy usunie mi to element wraz z tym co zostało zaalokowane?

@Edit
C/C++
#include <vector>
#include <iostream>
using namespace std;

class Pamieciozer {
public:
    vector < long long *> tab;
   
    Pamieciozer() {
        tab.resize( 10000 );
        for( auto & x: tab ) {
            long long * nowy = new long long;
            x = nowy;
        }
        tab.erase( tab.begin(), tab.end() );
    }
};

int main() {
    while( true ) {
        Pamieciozer terefere;
    }
}
A no właśnie nie działa... Taki programik zrobiłem do przetestowania co się z pamięcią robi w menedżerze. Erase nie usunie mi alokacji jaka jest pod adresem wskaźnika. Bez sensu -.-
P-131677
notabigthreat
» 2015-05-01 17:02:06
Zamiast wskaźników możesz użyć
unique_ptr < Klasa_bazowa >
.
Zachowuje się on bardzo podobnie do zwykłych wskaźników, tyle że gdy wskaźnik jest usuwany, obiekt jest niszczony. Do składowych dostajesz się normalnie, przy pomocy "strzałki" (" -> ").
Potrzebne jest wstawienie linijki:
C/C++
#include <memory>
Wskaźników takich nie można kopiować, bo każda kopia pod koniec usunęłaby obiekt i miałoby się kilka
delete
ów na jeden adres. Jeżeli zachodzi potrzeba, by kilka obiektów współdzieliło między sobą jakiś obiekt przez wskaźniki, alternatywą jest
shared_ptr
. Wydaje się, że musisz w dodatku zaznaczyć "Have g++ follow the C++11 ISO C++ language standard" w Settings -> Compiler. Dzięki temu rozwiązaniu, nie musisz zawracać sobie głowy usuwaniem tego-a-tamtego wtedy-a-wtedy. Erasowanie od razu wszystko załatwia.
Push wygląda jakoś tak (też pi razy oko):
C/C++
tanks.push_back( static_cast < unique_ptr < Tank >>( new Enemy( /* argumenty... */ ) ) );
P-131678
pekfos
» 2015-05-01 17:45:20
Erase nie usunie mi alokacji jaka jest pod adresem wskaźnika. Bez sensu -.-
To jest bez sensu..? Bez sensu jest większość tego kodu, działanie erase() ma sens, bo jest uniwersalne. Inaczej nie można by działać na wskaźnikach, których nie można zwalniać.

Push wygląda jakoś tak (też pi razy oko):
C/C++
tanks.push_back( static_cast < unique_ptr < Tank >>( new Enemy( /* argumenty... */ ) ) );
Słabo to wygląda
P-131679
Fireho
» 2015-05-01 19:54:44
-
delete
 usuwa obiekt wskazywany przez wskaźnik, ale zostawia wskaźnik w nienaruszonym staniem(nie ma on wartości
nullptr
, wciąż pokazuje na to samo miejsce)
-
erase
 usuwa sam wskaźnik z listy, ale nie zwalnia samego obiektu pokazywanego przez wskaźnik

Podsumowując: jak chcesz usunąć sam obiekt i wywalić wskaźnik na niego z kontenera to musisz dać wtedy
delete
 na wskaźnik(aby usunąć obiekt z pamięci) oraz
erase
 na dany element listy(aby usunąć wskaźnik z listy). Alternatywą jest użycie
std::unique_ptr
 jak wcześniej powiedziano, wtedy będziesz mógł dać samo
erase
 i destruktor zadba o zwalnianie pamięci.

No i najlepiej dodawać
std::unique_ptr
y do listy tak:
tanks.emplace_back( new Enemy( /* jakieś argumenty */ ) );
.
P-131681
« 1 » 2
  Strona 1 z 2 Następna strona