2012-11-04 29 views
51

Kontekst: Próbuję objąć głowę wskaźnikami, widzieliśmy je kilka tygodni temu w szkole i podczas dzisiejszej praktyki wpadłem na głupiego? problem, może to być dla ciebie bardzo proste, ale mam niewielkie lub żadne doświadczenie w programowaniu.Usuwanie wskaźnika w C++

Zdarzyło mi się sporo pytań na temat usuwania wskaźników, ale wszystkie wydają się być związane z usuwaniem klasy, a nie "prostym" wskaźnikiem (lub jakimkolwiek innym właściwym określeniem), oto kod I Próbuję uruchomić:

#include <iostream>; 

using namespace std; 

int main() { 
    int myVar, 
     *myPointer; 

    myVar = 8; 
    myPointer = &myVar; 

    cout << "delete-ing pointers " << endl; 
    cout << "Memory address: " << myPointer << endl; 

    // Seems I can't *just* delete it, as it triggers an error 
    delete myPointer; 
    cout << "myPointer: " << myPointer << endl; 
    // Error: a.out(14399) malloc: *** error for object 0x7fff61e537f4: 
    // pointer being freed was not allocated 
    // *** set a breakpoint in malloc_error_break to debug 
    // Abort trap: 6 

    // Using the new keyword befor deleting it works, but 
    // does it really frees up the space? 
    myPointer = new int; 
    delete myPointer; 
    cout << "myPointer: " << myPointer << endl; 
    // myPointer continues to store a memory address. 

    // Using NULL before deleting it, seems to work. 
    myPointer = NULL; 
    delete myPointer; 
    cout << "myPointer: " << myPointer << endl; 
    // myPointer returns 0. 

} 

Więc moje pytania to:

  1. Dlaczego nie pierwsza praca przypadek? Wydaje się być najprostszym sposobem użycia i usunięcia wskaźnika? Błąd mówi, że pamięć nie została przydzielona, ​​ale "cout" zwrócił adres.
  2. W drugim przykładzie błąd nie jest wyzwalany, ale robi cout wartości myPointer nadal zwraca adres pamięci?
  3. Czy # 3 naprawdę działa? Wydaje się pracować dla mnie, wskaźnik nie zapisuje już adresu, czy jest to właściwy sposób na usunięcie wskaźnika?

Przepraszam za długie pytanie, chciałem uczynić to jak najbardziej zrozumiałym, również powtórzyć, mam niewielkie doświadczenie programistyczne, więc jeśli ktoś mógłby odpowiedzieć na to używając warunków laika, byłoby to bardzo cenne!

+11

Powodem, dla którego pierwszy przykład nie występuje, jest błąd. Tylko "usuń" to, co "nowe". Nie jest również wymagane, aby wskaźnik ustawił się na wartość NULL po usunięciu go. Jeśli chcesz tam bezpieczeństwa, użyj inteligentnych wskaźników, które zwalniają pamięć i dają błędy, gdy próbujesz uzyskać do nich dostęp, gdy czegoś nie trzymają. – chris

+0

Hmm, nie jestem pewien, jakie są inteligentne wskaźniki, ale przyjrzę się temu, dzięki! – leopic

+1

W skrócie, robią to, co opisałem. Aby pomieścić coś nowego, nazywasz 'reset' i uwalnia on stare. Aby go uwolnić bez wymiany, nazywasz 'release'. Kiedy wykracza poza zakres, ulega zniszczeniu i może zwolnić pamięć w oparciu o jej typ. 'std :: unique_ptr' jest przeznaczone tylko dla jednego właściciela. 'std :: shared_ptr' uwalnia go, gdy ostatni właściciel przestaje być właścicielem zasobu. Są również wyjątkowo bezpieczne. Jeśli przydzielisz zasób z jednym, a następnie napotkasz wyjątek, zasób zostanie odpowiednio zwolniony. – chris

Odpowiedz

106

myVar = 8; //not dynamically allocated. Can't call delete on it. 
myPointer = new int; //dynamically allocated, can call delete on it. 

Pierwsza zmienna rozdzielono na stosie. Możesz wywołać usuwanie tylko w pamięci przydzielonej dynamicznie (na stercie) za pomocą operatora new.

3.

myPointer = NULL; 
    delete myPointer; 

Powyższy zrobił ogóle nic. Nic nie uwolniłeś, ponieważ wskaźnik wskazywał NULL.


Poniższe informacje nie powinny być zrobione:

myPointer = new int; 
myPointer = NULL; //leaked memory, no pointer to above int 
delete myPointer; //no point at all 

Ty wskazał na NULL, pozostawiając wycieku pamięci (nowa int Ci przydzielone). Powinieneś zwolnić pamięć, na którą wskazywałeś. Nie ma sposobu, aby uzyskać dostęp do tej przydzielonej new int, stąd wyciek pamięci.


Prawidłowy sposób:

myPointer = new int; 
delete myPointer; //freed memory 
myPointer = NULL; //pointed dangling ptr to NULL 

Lepszym sposobem:

Jeśli używasz C++, nie zastosowania surowych wskaźników.Zamiast tego użyj smart pointers, która może obsłużyć te rzeczy przy niewielkim obciążeniu. C++ 11 pochodzi z several.

+9

"Na stosie" jest szczegółem implementacji - takim, którego C++ w sposób unikalny wymienia. Bardziej poprawnym terminem jest "z automatycznym czasem przechowywania". (C++ 11, 3.7.3) cHao

+4

Dziękuję, wybrałem twoją odpowiedź na a) wyjaśnienie, co było nie tak i b) dając najlepszą praktykę, wielkie dzięki! – leopic

+0

Chciałbym dodać, bez względu na to, ile lat ma ten post (ze względu na googlersów), 'delete myPointer' deallocates' * myPointer', a 'myPointer' już nie wskazuje na nic. Zostanie zniszczony na końcu zakresu. – Tqn

7

Istnieje zasada w języku C++, dla każdego nowego jest usuwać.

  1. Dlaczego nie pierwsza praca przypadek? Wydaje się być najprostszym sposobem użycia i usunięcia wskaźnika? Błąd mówi, że pamięć nie została przydzielona, ​​ale "cout" zwrócił adres.

nowy nigdy nie jest wywoływany. Tak więc adres, w którym cout drukuje, to adres położenia pamięci myVar lub wartość przypisana do myPointer w tym przypadku. Pisząc:

myPointer = &myVar; 

mówisz:

myPointer = Adres gdzie dane w myVar jest przechowywany

  1. On w drugim przykładzie błąd nie jest wyzwalany, ale wykonanie cue wartości myPointer wciąż zwraca adres pamięci?

Zwraca adres wskazujący lokalizację pamięci, która została usunięta. Ponieważ najpierw utworzysz wskaźnik i przypiszesz jego wartość myPointer, po czym go usuniesz, a po trzecie - wydrukujesz. Więc jeśli nie przypisujesz innej wartości myPointer, usunięty adres pozostanie.

  1. Czy # 3 naprawdę działa? Wydaje się pracować dla mnie, wskaźnik nie zapisuje już adresu, czy jest to właściwy sposób na usunięcie wskaźnika?

NULL jest równe 0, usuwasz 0, więc nic nie usuwasz. I to jest logika, która drukuje 0 bo zrobiłeś:

myPointer = NULL; 

który wynosi:

myPointer = 0; 
4
  1. Próbujesz usunąć zmienną przydzielonego na stosie. Nie można tego zrobić
  2. Usunięcie wskaźnika nie powoduje rzeczywistego zniszczenia wskaźnika, tylko zajęta pamięć jest zwracana do systemu operacyjnego. Możesz uzyskać do niego dostęp, dopóki pamięć nie zostanie użyta dla innej zmiennej lub w inny sposób zmanipulowana. Dobrą praktyką jest ustawienie wskaźnika na NULL (0) po usunięciu.
  3. Usunięcie wskaźnika NULL nie powoduje usunięcia niczego.
10

Wskaźniki są podobne do normalnych zmiennych, ponieważ nie trzeba ich usuwać. Są one usuwane z pamięci po zakończeniu wykonywania funkcji i/lub po zakończeniu programu.

Można jednak użyć wskaźników przeznaczyć „” blok pamięci, na przykład tak:

int *some_integers = new int[20000] 

Ten przydzieli pamięci do 20000 całkowitymi. Przydatne, ponieważ stos ma ograniczony rozmiar i możesz chcieć zadzwonić z dużym obciążeniem "int" bez błędu przepełnienia stosu.

Za każdym razem, gdy wywołujesz nowe, powinieneś "usunąć" na końcu programu, ponieważ w przeciwnym razie dostaniesz wyciek pamięci, a niektóre przydzielone miejsca w pamięci nigdy nie zostaną zwrócone, aby inne programy mogły zostać użyte. Aby to zrobić:

delete [] some_integers; 

Nadzieję, że pomaga.

+1

Chcę po prostu dodać, że przydzielona pamięć zostanie zwrócona dla innych programów do użycia, ale tylko PO zakończeniu programu. – sk4l

1
int value, *ptr; 

value = 8; 
ptr = &value; 
// ptr points to value, which lives on a stack frame. 
// you are not responsible for managing its lifetime. 

ptr = new int; 
delete ptr; 
// yes this is the normal way to manage the lifetime of 
// dynamically allocated memory, you new'ed it, you delete it. 

ptr = nullptr; 
delete ptr; 
// this is illogical, essentially you are saying delete nothing. 
+1

Ponadto zapoznaj się z wykładem na temat ramek stosów http://www.youtube.com/watch?v=bjObm0hxIYY i http://www.youtube.com/watch?v=Rxvv9krECNww sprawie wskaźników. –

12

Uważam, że nie w pełni rozumiem, w jaki sposób działają wskaźniki.
Kiedy masz wskaźnik wskazujący jakiegoś pamięci istnieją trzy różne rzeczy trzeba zrozumieć:
- jest „to, co jest spiczasty” przez wskaźnik (pamięć)
- ten adres pamięci
- nie wszystkie wskaźniki muszą aby ich pamięć została usunięta: wystarczy usunąć pamięć, która została przydzielona dynamicznie (użyty operator new).

Imagine:

int *ptr = new int; 
// ptr has the address of the memory. 
// at this point, the actual memory doesn't have anything. 
*ptr = 8; 
// you're assigning the integer 8 into that memory. 
delete ptr; 
// you are only deleting the memory. 
// at this point the pointer still has the same memory address (as you could 
// notice from your 2nd test) but what inside that memory is gone! 

Kiedy zrobiłeś

ptr = NULL; 
// you didn't delete the memory 
// you're only saying that this pointer is now pointing to "nowhere". 
// the memory that was pointed by this pointer is now lost. 

C++ pozwala, aby spróbować delete wskaźnik wskazujący na null ale faktycznie nie robić nic, po prostu nie daje jakikolwiek błąd.

+1

Dzięki, to było super pomocne, myślałem, że mam usunąć wszystkie wskaźniki, nie wiedziałem, że tylko dla tych, którzy byli nowi, dzięki. – leopic

Powiązane problemy