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

Stos na wskaźnikach. Czy używać delete i jak to zrobić.

Ostatnio zmodyfikowano 2017-02-16 19:21
Autor Wiadomość
MDK
Temat założony przez niniejszego użytkownika
Stos na wskaźnikach. Czy używać delete i jak to zrobić.
» 2017-02-14 22:58:40
Napisałem sobie program stos i zastanawiam się, czy powinienem użyć funkcji delete przy usuwaniu elementu, a jeśli tak, jak to właściwie zrobić i po co. Chodzi mi o funkcję "pop". Czy jak nie usunę elementu, to on może wywołać jakąś ucieczkę pamięci, albo zawieszenie się czegoś? Poniżej wklejam kod - wszelkie dodatkowe rady też mile widziane.
C/C++
#include <iostream>

using namespace::std;

class poleSt {
public:
    int wartosc = 0;
    int licznik = 0;
    poleSt * wsk = nullptr;
};

poleSt * push( poleSt * wsk2, int & licznik2 ) {
    poleSt * wsk1;
    wsk1 = new poleSt;
    wsk1->licznik = ++licznik2;
    cout << "Podaj wartosc: " << endl;
    cin >> wsk1->wartosc;
    wsk1->wsk = wsk2;
    return wsk1;
}

poleSt * pop( poleSt * wsk2 ) {
    if( wsk2 == nullptr ) {
        cout << "Brak elementow do usuniecia " << endl;
    }
    if( wsk2 != nullptr ) wsk2 = wsk2->wsk;
   
    return wsk2;
}

void show( poleSt * wsk2 ) {
    do {
        if( wsk2 == nullptr ) {
            cout << "Brak elementow na stosie " << endl;
            break;
        }
        if( wsk2->wsk == nullptr ) {
            cout << "Pozycja: " << wsk2->licznik << " ma wartosc: " << wsk2->wartosc << endl;
            break;
        }
        cout << "Pozycja: " << wsk2->licznik << " ma wartosc: " << wsk2->wartosc << endl;
        wsk2 = wsk2->wsk;
    } while( wsk2->wsk != nullptr );
   
}

int main() {
   
    int licznikI = 0, wybor = 0;
    poleSt * wsk0;
    wsk0 = nullptr;
   
    do {
        cout << "Wybierz, co chcesz zrobic: " << endl;
        cout << " 1 - push, 2 - pop, 3 - pokaz, 4 - wyjdz " << endl;
        cin >> wybor;
       
        switch( wybor ) {
        case 1: wsk0 = push( wsk0, licznikI );
            break;
        case 2: wsk0 = pop( wsk0 );
            break;
        case 3: show( wsk0 );
            break;
        case 4: cout << " Koniec " << endl;
        }
    }
    while( wybor != 4 );
   
    return 0;
}


P-157720
mokrowski
» 2017-02-15 04:14:41
Jeśli chodzi o delete to oczywiście że należy go użyć. Teraz w programie wycieka pamięć. W push(), masz także niezbyt faje odkładanie skutków ubocznych w licznik2. Funkcja coś zwraca ale jednocześnie zmienia argument bo jest przekazany przez referencję. W każdym węźle poleSt, odkładasz teraz nadmiarowo licznik a powinien być 1 dla całości stosu.
P-157728
MDK
Temat założony przez niniejszego użytkownika
» 2017-02-16 00:05:36
Dzięki za radę z licznikiem. Rzeczywiście, to było zupełnie bez sensu.
Dodałem też delete. Poniżej wstawiam poprawiony kod i proszę o opinię, czy dobrze go zastosowałem. I czy nic więcej nie trzeba zrobić.

C/C++
#include <iostream>

using namespace::std;

class poleSt {
public:
    int wartosc = 0;
    poleSt * wsk = nullptr;
};

poleSt * push( poleSt * wsk2, int & licznik2 ) {
    poleSt * wsk1;
    wsk1 = new poleSt;
    licznik2++;
    cout << "Podaj wartosc: " << endl;
    cin >> wsk1->wartosc;
    wsk1->wsk = wsk2;
    return wsk1;
}

poleSt * pop( poleSt * wsk2, int & licznik2 ) {
    if( wsk2 == nullptr ) {
        cout << "Brak elementow do usuniecia " << endl;
    }
    if( wsk2 != nullptr ) {
        delete wsk2;
        wsk2 = wsk2->wsk;
        licznik2--;
    }
    return wsk2;
}

void show( poleSt * wsk2, int licznik2 ) {
    do {
        if( wsk2 == nullptr ) {
            cout << "Brak elementow na stosie " << endl;
            break;
        }
        if( wsk2->wsk == nullptr ) {
            cout << "Pozycja: " << licznik2 << " ma wartosc: " << wsk2->wartosc << endl;
            break;
        }
        cout << "Pozycja: " << licznik2 << " ma wartosc: " << wsk2->wartosc << endl;
        wsk2 = wsk2->wsk; licznik2--;
    } while( wsk2 != nullptr );
   
}

int main() {
   
    int licznikI = 0, wybor = 0;
    poleSt * wsk0;
    wsk0 = nullptr;
   
    do {
        cout << "Wybierz, co chcesz zrobic: " << endl;
        cout << " 1 - push, 2 - pop, 3 - pokaz, 4 - wyjdz " << endl;
        cin >> wybor;
       
        switch( wybor ) {
        case 1: wsk0 = push( wsk0, licznikI );
            break;
        case 2: wsk0 = pop( wsk0, licznikI );
            break;
        case 3: show( wsk0, licznikI );
            break;
        case 4: cout << " Koniec " << endl;
        }
    }
    while( wybor != 4 );
   
    return 0;
}
P-157795
mokrowski
» 2017-02-16 11:55:33
Ok, pamięć Ci już nie cieknie ale... Przeczytaj jeszcze raz kod...
C/C++
if( wsk2 != nullptr ) {
    delete wsk2;
    wsk2 = wsk2->wsk;
    licznik2--;
Widać gołym okiem że najpierw dealokujesz (delete) pamięć pod wskaźnikiem wsk2 a linijkę później go używasz! Na nieszczęście często to działa ale to duży błąd!
... i zacytuję jeszcze komunikat narzędzia cppcheck które informuje że ...

Checking x.cpp ...
[x.cpp:27]: (error) Dereferencing 'wsk2' after it is deallocated / released
[x.cpp:30]: (error) Returning/dereferencing 'wsk2' after it is deallocated / released
Interesuje Cię zapamiętanie wsk2->wsk w zmiennej tymczasowej, usunięcie (delete) wsk2, przypisanie do wsk2 wartości zmiennej tymczasowej.
P-157823
MDK
Temat założony przez niniejszego użytkownika
» 2017-02-16 18:05:00
Chyba powinno być dobrze. Tylko zastawiam się, czy powinienem też używać delete na wskaźniku tymczasowym (w poniższym kodzie używam), który utworzyłem. W końcu dostaje on adres obiektu stworzonego dynamicznie.

C/C++
#include <iostream>

using namespace::std;

class poleSt {
public:
    int wartosc = 0;
    poleSt * wsk = nullptr;
};

poleSt * push( poleSt * wsk2, int & licznik2 ) {
    poleSt * wsk1;
    wsk1 = new poleSt;
    licznik2++;
    cout << "Podaj wartosc: " << endl;
    cin >> wsk1->wartosc;
    wsk1->wsk = wsk2;
    return wsk1;
}

poleSt * pop( poleSt * wsk2, int & licznik2 ) {
    if( wsk2 == nullptr ) {
        cout << "Brak elementow do usuniecia " << endl;
    }
    if( wsk2 != nullptr ) {
        poleSt * wskTymczasowy;
        wskTymczasowy = wsk2->wsk;
        delete wsk2;
        wsk2 = wskTymczasowy;
        delete wskTymczasowy;
        licznik2--;
    }
    return wsk2;
}

void show( poleSt * wsk2, int licznik2 ) {
    do {
        if( wsk2 == nullptr ) {
            cout << "Brak elementow na stosie " << endl;
            break;
        }
        if( wsk2->wsk == nullptr ) {
            cout << "Pozycja: " << licznik2 << " ma wartosc: " << wsk2->wartosc << endl;
            break;
        }
        cout << "Pozycja: " << licznik2 << " ma wartosc: " << wsk2->wartosc << endl;
        wsk2 = wsk2->wsk; licznik2--;
    } while( wsk2 != nullptr );
   
}

int main() {
   
    int licznikI = 0, wybor = 0;
    poleSt * wsk0;
    wsk0 = nullptr;
   
    do {
        cout << "Wybierz, co chcesz zrobic: " << endl;
        cout << " 1 - push, 2 - pop, 3 - pokaz, 4 - wyjdz " << endl;
        cin >> wybor;
       
        switch( wybor ) {
        case 1: wsk0 = push( wsk0, licznikI );
            break;
        case 2: wsk0 = pop( wsk0, licznikI );
            break;
        case 3: show( wsk0, licznikI );
            break;
        case 4: cout << " Koniec " << endl;
        }
    }
    while( wybor != 4 );
   
    return 0;
}
P-157837
mokrowski
» 2017-02-16 18:49:38
Oczywiście że nie. To w jaki sposób został zaalokowany ten wskaźnik to jedno. Drugie to czy ma tam być usuwany czy nie. W tym przypadku nie. Zostawiam komentarz. Linijka z komentarzem ma być usunięta.
C/C++
if( wsk2 != nullptr ) {
    poleSt * wskTymczasowy;
    wskTymczasowy = wsk2->wsk;
    delete wsk2;
    wsk2 = wskTymczasowy;
    //delete wskTymczasowy;
    licznik2--;
}
P-157842
MDK
Temat założony przez niniejszego użytkownika
» 2017-02-16 19:21:41
Dzięki za pomoc, dużo mi to dało.
P.S.
Mam jeszcze jedno pytanie. Czy przed zamknięciem programu, jak mam jakieś elementy na stosie, to muszę też je usunąć za pomocą delete?
P-157843
« 1 »
  Strona 1 z 1