2009-04-01 13 views
108

Zawsze zastanawiałem się, dlaczego automatyczne ustawienie wskaźnika na NULL po delete nie jest częścią standardu. Jeśli zostanie to zajęte, wówczas wiele awarii z powodu nieprawidłowego wskaźnika nie wystąpi. Ale powiedział, że mogę wymyślić kilka powodów, dla których średnia byłaby ograniczona w ten sposób:Dlaczego nie usunąć ustawić wskaźnik na NULL?

  1. Wydajność:

    Dodatkowe wskazówki mogłyby spowolnić działanie delete.

  2. Czy to możliwe ze względu na wskaźniki const.

    Potem znowu standard mógł zrobić coś dla tego szczególnego przypadku, jak sądzę.

Czy ktoś zna dokładnych przyczyn nie pozwalając tym?

Odpowiedz

138

Stroustrup himself odpowiedzi. Fragment:

C++ wyraźnie umożliwia implementację z kasowania do zera z lwartością argumentu, a miałem nadzieję że implementacje to zrobić, ale ten pomysł nie wydaje się mieć stają się popularne realizatorzy.

Ale głównym problemem, który podnosi, że argument delete nie musi być l-wartości.

12

Ponieważ tak naprawdę nie ma takiej potrzeby, a także dlatego, że wymagałoby to usunięcia wskaźnika do wskaźnika, a nie tylko wskaźnika.

+1

Lub odniesienie do wskaźnika. –

+0

prawda, ale to spowodowałoby ten sam napowietrzny – snemarch

5

Jeśli masz tablicę wskaźników, a twoją drugą akcją jest usunięcie pustej tablicy, to nie ma żadnego punktu, w którym każda wartość zostanie ustawiona na wartość null, gdy pamięć zostanie zwolniona. Jeśli chcesz, aby była pusta ... napisz do niej null :)

54

Po pierwsze, ustawienie na null wymagałoby zmiennej przechowywanej w pamięci. To prawda, że ​​zazwyczaj masz wskaźnik w zmiennej, ale czasami możesz chcieć usunąć obiekt z właśnie wyliczonego adresu. Byłoby to niemożliwe przy usuwaniu "nullifying".

Następnie pojawia się wydajność. Możliwe, że napisałeś kod w taki sposób, że wskaźnik zniknie z zakresu natychmiast po wykonaniu delete. Wypełnienie go wartością zerową to strata czasu. A C++ to język, w którym "nie potrzebujesz tego? To nie musisz za to płacić".

Jeśli potrzebujesz bezpieczeństwa, możesz skorzystać z szerokiej gamy inteligentnych wskaźników lub możesz pisać własne - lepsze i mądrzejsze.

+4

Dobry adres obliczany pod adresem, nawet jeśli jest to coś, czego często nie widzisz – snemarch

+0

czy mówisz o nowym miejscu, kiedy mówisz, że czasami możesz chcieć usunąć obiekt po prostu obliczony adres. ??? – Destructor

+0

@PravasiMeet Nie, mam na myśli coś takiego jak "delete (ptr + i)" – sharptooth

35

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 dla usunięcia został ustawiony na wartość null, ale wszystkie pozostałe wskaźniki nie. Wskaźnik to nic innego jak adres, liczba. Równie dobrze może być int z operacją dereferencji. Chodzi mi o to, że musiałbyś skanować każdy pojedynczy wskaźnik, aby znaleźć te, które odwołują się do tej samej pamięci, którą właśnie skasowałeś, i usuwać je również. Intensywne obliczeniowo byłoby przeskanowanie wszystkich wskaźników dla tego adresu i wyeliminowanie ich, ponieważ język nie jest do tego przystosowany. (Chociaż niektóre inne języki strukturyzują swoje odniesienia, aby osiągnąć podobny cel w inny sposób.)

17

Wskaźnik można zapisać w więcej niż jednej zmiennej, ustawienie jednego z nich na NULL nadal pozostawi nieprawidłowe wskaźniki w innych zmiennych. Więc nie zyskujesz zbyt wiele, bardziej prawdopodobne jest stworzenie fałszywego poczucia bezpieczeństwa.

Oprócz tego, można stworzyć własną funkcję, która robi to, co chcesz:

template<typename T> 
void deleten(T *&ptr) { 
    delete ptr; 
    ptr = NULL; 
} 
4

C++ pozwala na zdefiniowanie własnego operatora new i delete tak, że na przykład oni korzystać z własnego basenu przydzielania. Jeśli to zrobisz, możliwe będzie użycie nowych i usuwanie z rzeczami, które nie są ściśle adresami, ale powiedzą indeksy w twojej tablicy puli. W tym kontekście wartość NULL (0) może mieć znaczenie prawne (odnoszące się do pierwszego elementu w puli).
Tak więc po usunięciu zbioru NULL automatycznie do jego argumentu nie zawsze ma to znaczenie - ustaw wartość na niepoprawną wartość. Błędna wartość nie zawsze musi mieć wartość NULL.

3

Ustawienie wskaźnika na NULL automatycznie nie rozwiązałoby większości problemów ze złym użyciem wskaźnika. Jedyną awarią, której można uniknąć, jest próba dwukrotnego usunięcia. Co jeśli wywołasz funkcję członka na takim wskaźniku? Wciąż się zawiesza (zakładając, że uzyskuje dostęp do zmiennych składowych). C++ nie ogranicza możliwości wywoływania jakiejkolwiek funkcji na wskaźnikach NULL, ani nie powinno tego robić z punktu widzenia wydajności.

4

Filozofia języka C++ to "zapłać za nią tylko wtedy, gdy jej użyjesz". Myślę, że to może odpowiedzieć na twoje pytanie.

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

7

delete jest używany głównie w destruktorach, w którym to przypadku ustawienie członka na NULL jest bezcelowe. Kilka linii później, przy zamknięciu }, członek już nie istnieje. W operatorach przypisania, po typowym usunięciu następuje zwykle przypisanie.

Również byłoby uczynić następujący kod nielegalnej:

T* const foo = new T; 
delete foo; 
4

Oto kolejny powód; Przypuśćmy, że delete ustawia jego argument na NULL:

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

Czy bar powinien być ustawiony na NULL? Czy możesz to uogólnić?

-2

Widzę ludzi, którzy dają dziwne odpowiedzi na to pytanie.

ptr = NULL; Jak taka prosta instrukcja może spowodować opóźnienie w działaniu?

Inna odpowiedź brzmi: możemy mieć wiele wskaźników wskazujących na to samo miejsce pamięci. Na pewno możemy.W tym przypadku operacja usunięcia na jednym wskaźniku spowodowałaby, że tylko wskaźnik NULL (jeśli delete powodowałby wskaźnik NULL), a drugi wskaźnik byłby nie-NULL i wskazywałby lokalizację pamięci, która jest wolna.

Rozwiązaniem tego problemu powinno być usunięcie wszystkich wskaźników wskazujących tę samą lokalizację. Wewnętrznie powinien sprawdzić, czy pamięć jest już zwolniona, nie wolno. Ustaw tylko wskaźnik NULL.

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

Powiązane problemy