Dlaczego nie usuń, ustawiając wskaźnik na NULL?

129

Zawsze zastanawiałem się, dlaczego automatyczne ustawienie wskaźnika na NULL po usunięciu nie jest częścią standardu. Jeśli zostanie to rozwiązane, wiele awarii z powodu nieprawidłowego wskaźnika nie wystąpi. Powiedziawszy jednak, że przychodzi mi do głowy kilka powodów, dla których norma miałaby to ograniczyć:

  1. Występ:

    Dodatkowa instrukcja może spowolnić deletedziałanie.

  2. Czy to może być spowodowane constwskazówkami.

    Z drugiej strony, myślę, że standard mógłby coś zrobić w tym szczególnym przypadku.

Czy ktoś zna dokładne powody, dla których na to nie pozwala?

aJ.
źródło

Odpowiedzi:

153

Sam Stroustrup odpowiada. Fragment:

C ++ jawnie zezwala implementacji delete na wyzerowanie operandu l-wartości i miałem nadzieję, że implementacje to zrobią, ale ten pomysł nie wydaje się być popularny wśród implementatorów.

Ale głównym problemem, który podnosi, jest to, że argument delete nie musi być lwartością.

Dan Olson
źródło
Myślę, że przydałoby się więcej wyjaśnień. Nie jestem nawet pewien, co mówi ... Przypuszczam, że będę musiał wrócić później, kiedy będę mógł poświęcić kilka godzin na zbadanie tego, zanim to zrozumiem. Możesz też wyjaśnić odpowiedź, która pomoże nam szybciej zrozumieć.
Gabriel Staples
66

Po pierwsze, ustawienie wartości null wymagałoby zmiennej przechowywanej w pamięci. To prawda, że ​​zwykle masz wskaźnik w zmiennej, ale czasami możesz chcieć usunąć obiekt pod właśnie obliczonym adresem. Byłoby to niemożliwe przy „anulowaniu” usuwania.

Potem przychodzi wydajność. Być może napisałeś kod w taki sposób, że wskaźnik wyjdzie poza zakres natychmiast po zakończeniu usuwania . Wypełnienie go zerami to tylko strata czasu. A C ++ to język z ideologią "nie potrzebujesz tego? Wtedy nie musisz za to płacić".

Jeśli potrzebujesz bezpieczeństwa, masz do dyspozycji szeroką gamę inteligentnych wskazówek lub możesz napisać własne - lepsze i sprytniejsze.

ostry
źródło
5
Dobra uwaga, jeśli chodzi o obliczony adres, nawet jeśli jest to coś, czego nie widzisz często
snemarch
czy mówisz o umieszczeniu nowego, kiedy mówisz, że czasami możesz chcieć usunąć obiekt pod właśnie obliczonym adresem. ???
Destructor
@PravasiMeet Nie, mam na myśli coś w styludelete (ptr + i)
ostry
40

Możesz mieć wiele wskaźników wskazujących na tę pamięć. Stworzyłoby to fałszywe poczucie bezpieczeństwa, gdyby wskaźnik określony do usunięcia został ustawiony na null, ale wszystkie inne wskaźniki nie. Wskaźnik to nic innego jak adres, liczba. Równie dobrze może to być int z operacją wyłuskiwania. Chodzi mi o to, że musiałbyś również przeskanować każdy pojedynczy wskaźnik, aby znaleźć te, które odwołują się do tej samej pamięci, którą właśnie usunąłeś, i je również usunąć. Przeszukanie wszystkich wskaźników dla tego adresu i ich wyeliminowanie wymagałoby dużej mocy obliczeniowej, ponieważ język nie jest do tego przeznaczony. (Chociaż niektóre inne języki układają swoje odniesienia, aby osiągnąć podobny cel w inny sposób).

AaronLS
źródło
19

Wskaźnik można zapisać w więcej niż jednej zmiennej, ustawienie jednej z nich na NULL nadal pozostawiłoby nieprawidłowe wskaźniki w innych zmiennych. Więc tak naprawdę niewiele zyskujesz, bardziej prawdopodobne jest, że stworzysz fałszywe poczucie bezpieczeństwa.

Poza tym możesz stworzyć własną funkcję, która robi to, co chcesz:

template<typename T>
void deleten(T *&ptr) {
  delete ptr;
  ptr = NULL;
}
sth
źródło
12

Ponieważ tak naprawdę nie ma takiej potrzeby, a ponieważ wymagałoby to usunięcia, przyjmując wskaźnik do wskaźnika, a nie tylko wskaźnik.

snemarch
źródło
prawda, ale skutkowałoby to tym samym narzutem
snemarch
7

deletejest używany głównie w destruktorach, w takim przypadku ustawienie elementu na NULL jest bezcelowe. Kilka linijek później, na zakończenie }, członek już nie istnieje. W operatorach przypisania po usunięciu zwykle i tak następuje przypisanie.

Ponadto spowodowałoby to, że następujący kod byłby nielegalny:

T* const foo = new T;
delete foo;
MSalters
źródło
6

Oto kolejny powód; załóżmy, że delete ustawia swój argument na NULL:

int *foo = new int;
int *bar = foo;
delete foo;

Czy pasek powinien zostać ustawiony na NULL? Czy możesz to uogólnić?

SingleNegationElimination
źródło
5

Jeśli masz tablicę wskaźników, a drugą czynnością jest usunięcie pustej tablicy, nie ma sensu ustawiać każdej wartości na null, gdy pamięć ma zostać zwolniona. Jeśli chcesz, aby był pusty .. napisz do niego null :)

Martwe konto
źródło
4

C ++ pozwala na zdefiniowanie własnego operatora new i delete, aby na przykład używał własnego alokatora puli. Jeśli to zrobisz, możesz użyć new i delete z rzeczami, które nie są ściśle adresami, ale mówią indeksy w tablicy puli. W tym kontekście wartość NULL (0) może mieć znaczenie prawne (odnosząc się do pierwszej pozycji w puli).
Zatem automatyczne ustawienie parametru delete na NULL nie zawsze ma znaczenie - ustawia wartość na nieprawidłową wartość. Nieprawidłowa wartość nie zawsze może wynosić NULL.

shoosh
źródło
4

Filozofia C ++ to „płać za to tylko wtedy, gdy go używasz”. Myślę, że to może odpowiedzieć na twoje pytanie.

Czasami możesz mieć własną stertę, która odzyska usuniętą pamięć ... lub czasami wskaźnik nie jest własnością żadnej zmiennej. Lub wskaźnik przechowywany w kilku zmiennych - można wyzerować tylko jedną z nich.
Jak widać, ma wiele problemów i możliwych problemów.

bayda
źródło
3

Automatyczne ustawienie wskaźnika na NULL nie rozwiązałoby większości problemów związanych z niewłaściwym użyciem wskaźnika. Jedyną awarią, której by uniknąć, jest dwukrotna próba usunięcia. A co, jeśli wywołasz funkcję składową na takim wskaźniku? Nadal by się zawiesił (zakładając, że uzyskuje dostęp do zmiennych składowych). C ++ nie ogranicza możliwości wywoływania żadnej funkcji na wskaźnikach NULL, ani nie powinien tego robić z punktu widzenia wydajności.


źródło
-1

Widzę ludzi, którzy udzielają dziwnych odpowiedzi na to pytanie.

ptr = NULL; W jaki sposób takie proste stwierdzenie może spowodować opóźnienie wydajności?

Inna odpowiedź mówi, że możemy mieć wiele wskaźników wskazujących na to samo miejsce w pamięci. Na pewno możemy. W tym przypadku operacja usuwania na jednym wskaźniku spowodowałaby, że tylko ten wskaźnik miałby wartość NULL (gdyby usuwanie powodowało, że wskaźnik miał wartość NULL), a drugi wskaźnik byłby inny niż NULL i wskazywał na wolne miejsce w pamięci.

Rozwiązanie powinno polegać na tym, że użytkownik powinien usunąć wszystkie wskaźniki wskazujące na tę samą lokalizację. Wewnętrznie powinien sprawdzić, czy pamięć jest już zwolniona, niż nie zwolnić. Tylko ustaw wskaźnik na NULL.

Stroustrup mógł zaprojektować usuwanie, aby działało w ten sposób. Myślał, że zajmą się tym programiści. Więc zignorował.

Nimesh Bapodra
źródło