Mamy teraz C ++ 11 z wieloma nowymi funkcjami. Ciekawym i mylącym (przynajmniej dla mnie) jest nowy nullptr
.
Cóż, nie trzeba już nieprzyjemnego makra NULL
.
int* x = nullptr;
myclass* obj = nullptr;
Nadal nie rozumiem, jak nullptr
działa. Na przykład artykuł w Wikipedii mówi:
C ++ 11 naprawia to, wprowadzając nowe słowo kluczowe, które ma służyć jako wyróżniona stała wskaźnika zerowego: nullptr. Jest typu nullptr_t , który jest domyślnie konwertowalny i porównywalny z dowolnym typem wskaźnika lub typem wskaźnika do elementu. Nie jest domyślnie konwertowalny ani porównywalny z typami integralnymi, z wyjątkiem bool.
Jak to jest słowo kluczowe i instancja typu?
Czy masz też inny przykład (oprócz Wikipedii), gdzie nullptr
jest lepszy od starego, dobrego 0
?
nullptr
służy również do reprezentowania pustego odwołania dla zarządzanych uchwytów w C ++ / CLI.nullptr_t
zagwarantowane jest posiadanie tylko jednego członkanullptr
? Tak więc, jeśli funkcja została zwróconanullptr_t
, to kompilator już wie, która wartość zostanie zwrócona, niezależnie od treści funkcji?std::nullptr_t
Można utworzyć instancję @AaronMcDaid , ale wszystkie instancje będą identyczne,nullptr
ponieważ typ jest zdefiniowany jakotypedef decltype(nullptr) nullptr_t
. Uważam, że głównym powodem tego typu jest to, że funkcje mogą być przeciążone specjalnie w celu przechwycenianullptr
, jeśli to konieczne. Zobacz tutaj przykład.Odpowiedzi:
To nie jest zaskakujące. Zarówno
true
ifalse
są wyszukiwane i jak literały mają typ (bool
).nullptr
jest literalnym wskaźnikiem typustd::nullptr_t
i jest wartością (nie można użyć jego adresu&
).4.10
o konwersji wskaźnika mówi, że wartość typustd::nullptr_t
jest stałą zerowego wskaźnika i że można przekształcić integralną stałą zerowego wskaźnikastd::nullptr_t
. Odwrotny kierunek jest niedozwolony. Pozwala to na przeładowanie funkcji zarówno wskaźników, jak i liczb całkowitych oraz przekazywanie wnullptr
celu wybrania wersji wskaźnika. PomijanieNULL
lub0
mylący wybórint
wersji.Rzutowanie
nullptr_t
na typ integralny wymagareinterpret_cast
i ma taką samą semantykę jak rzutowanie(void*)0
na typ integralny (zdefiniowano implementację odwzorowania). Niereinterpret_cast
można przekonwertowaćnullptr_t
na dowolny typ wskaźnika. W miarę możliwości polegaj na domniemanej konwersji lub użyjstatic_cast
.Standard wymaga,
sizeof(nullptr_t)
BEsizeof(void*)
.źródło
cond ? nullptr : 0;
. Usunięto z mojej odpowiedzi.NULL
nie ma takiej gwarancji0
. Może być0L
, w którym to przypadku wezwanievoid f(int); void f(char *);
będzie dwuznaczne.nullptr
zawsze faworyzuje wersję wskaźnika i nigdy nie wywołuje tejint
. Zauważ też, żenullptr
można go zamienić nabool
(projekt mówi, że o4.12
).int
wersję. Alef(0L)
jest niejednoznaczny, ponieważlong -> int
ilong -> void*
oba są równie kosztowne. Jeśli więc0L
w kompilatorze znajduje się NULL , wywołanief(NULL)
będzie dwuznaczne, biorąc pod uwagę te dwie funkcje. Nie tak znullptr
oczywiście.(void*)0
w C ++. Można go jednak zdefiniować jako dowolną stałą zerową wskaźnika, którąnullptr
spełnia dowolna stała całkowa o wartości 0 . Więc zdecydowanie nie będzie, ale może . (Zapomniałeś mi pingować btw ..)Od nullptr: Bezpieczny i wyraźny wskaźnik zerowy typu :
Inne referencje:
template
źródło
Dlaczego nullptr w C ++ 11? Co to jest? Dlaczego NULL nie wystarcza?
Ekspert C ++, Alex Allain, mówi doskonale tutaj (moje podkreślenie zostało pogrubione):
Allain kończy swój artykuł:
(Moje słowa):
Na koniec nie zapominaj, że
nullptr
to obiekt - klasa. Można go używać w dowolnym miejscu, w którymNULL
był używany wcześniej, ale jeśli z jakiegoś powodu potrzebujesz jego typu, można go wyodrębnićdecltype(nullptr)
lub opisać bezpośrednio jakostd::nullptr_t
, co jest po prostu jednymtypedef
zdecltype(nullptr)
.Bibliografia:
źródło
Gdy masz funkcję, która może odbierać wskaźniki do więcej niż jednego typu, wywoływanie jej za pomocą
NULL
jest niejednoznaczne. Sposób, w jaki teraz to działa, jest bardzo hackerski, przyjmując int i zakładając, że jestNULL
.W
C++11
byłbyś w stanie przeciążenianullptr_t
, tak żeptr<T> p(42);
byłoby błędem kompilacji zamiast run-timeassert
.źródło
NULL
zostanie zdefiniowane jako0L
?nullptr
nie może być przypisany do typu integralnego, takiego jakint
typ wskaźnika, ale tylko; albo wbudowany typ wskaźnika, jakint *ptr
lub inteligentny wskaźnik, taki jakstd::shared_ptr<T>
Uważam, że jest to ważne rozróżnienie, ponieważ
NULL
nadal można je przypisać zarówno do typu integralnego, jak i do wskaźnika, podobnie jakNULL
makro rozwinięte, do0
którego może służyć zarówno jako wartość początkowa,int
jak i wskaźnik.źródło
NULL
nie gwarantuje się rozszerzenia do0
.Tak. Jest to także (uproszczony) przykład ze świata rzeczywistego, który pojawił się w naszym kodzie produkcyjnym. Wyróżniał się tylko dlatego, że gcc był w stanie wydać ostrzeżenie podczas kompilacji krzyżowej na platformę o różnej szerokości rejestru (wciąż nie jestem pewien, dlaczego tylko przy kompilacji krzyżowej z x86_64 na x86, ostrzega
warning: converting to non-pointer type 'int' from NULL
):Rozważ ten kod (C ++ 03):
Daje to wynik:
źródło
Cóż, inne języki mają zastrzeżone słowa, które są instancjami typów. Python, na przykład:
Jest to właściwie dość dokładne porównanie, ponieważ
None
zwykle jest używane do czegoś, co nie zostało zainicjalizowane, ale jednocześnie porównania, takie jakNone == 0
fałszywe.Z drugiej strony, w zwykłym C,
NULL == 0
zwróciłoby prawdę IIRC, ponieważNULL
jest to tylko makro zwracające 0, które zawsze jest nieprawidłowym adresem (AFAIK).źródło
NULL
to makro, które rozwija się do zera, stałe zerowanie rzutowane na wskaźnik daje wskaźnik zerowy. Wskaźnik zerowy nie musi być zerowy (ale często jest), zero nie zawsze jest nieprawidłowym adresem, a niestałe zero rzutowane na wskaźnik nie musi być zerowe, a wskaźnik zerowy rzutowany na liczba całkowita nie musi wynosić zero. Mam nadzieję, że wszystko w porządku, nie zapominając o niczym. Odniesienie: c-faq.com/null/null2.htmlJest to słowo kluczowe, ponieważ standard określa je jako takie. ;-) Zgodnie z najnowszym publicznym projektem (n2914)
Jest to użyteczne, ponieważ nie jest domyślnie konwertowane na wartość całkowitą.
źródło
Powiedzmy, że masz funkcję (f), która jest przeciążona, aby przyjąć zarówno int, jak i char *. W wersjach wcześniejszych niż C ++ 11, jeśli chcesz wywołać go wskaźnikiem zerowym i użyjesz wartości NULL (tj. Wartości 0), wtedy wywołałbyś tę przeciążoną dla int:
Prawdopodobnie nie tego chciałeś. C ++ 11 rozwiązuje to za pomocą nullptr; Teraz możesz napisać:
źródło
Pozwól, że najpierw przedstawię ci implementację nieskomplikowanych
nullptr_t
nullptr
jest subtelnym przykładem idiomu typu zwracanego typu, aby automatycznie wydedukować pusty wskaźnik poprawnego typu w zależności od typu instancji, do której przypisuje.nullptr
jest przypisywany wskaźnikowi liczby całkowitej,int
tworzona jest instancja typu funkcji konwersji opartej na szablonie. To samo dotyczy wskaźników metod.nullptr
literał jest liczbą całkowitą o wartości zero, nie można użyć jego adresu, który osiągnęliśmy, usuwając i operator.Dlaczego
nullptr
przede wszystkim potrzebujemy ?NULL
ma z tym jakiś problem, jak poniżej:1️⃣ Domniemana konwersja
2️⃣ Dwuznaczność wywoływania funkcji
3️⃣ Przeciążenie konstruktora
String s((char*)0))
.źródło
0 było jedyną liczbą całkowitą, która mogła być używana jako inicjalizator bez rzutowania dla wskaźników: nie można inicjować wskaźników z innymi wartościami całkowitymi bez rzutowania. Możesz uznać 0 za singleton consexpr składniowo podobny do literału liczby całkowitej. Może zainicjować dowolny wskaźnik lub liczbę całkowitą. Ale, co zaskakujące, przekonasz się, że nie ma wyraźnego typu: jest
int
. Dlaczego więc 0 może inicjować wskaźniki, a 1 nie? Praktyczną odpowiedzią było to, że potrzebujemy sposobu definiowania wartości zerowej wskaźnika, a bezpośrednia niejawna konwersjaint
na wskaźnik jest podatna na błędy. W ten sposób 0 stał się prawdziwą dziwaczną bestią z czasów prehistorycznych.nullptr
został zaproponowany jako rzeczywista reprezentacja wartości zerowej constexpr dla inicjalizacji wskaźników. Nie można go użyć do bezpośredniej inicjalizacji liczb całkowitych i wyeliminować dwuznaczności związane z definiowaniem.NULL
w kategoriach 0.nullptr
można go zdefiniować jako bibliotekę przy użyciu standardowej składni, ale semantycznie wygląda na brakujący składnik podstawowy.NULL
jest teraz przestarzałe na korzyśćnullptr
, chyba że jakaś biblioteka zdecyduje się go zdefiniować jakonullptr
.źródło
Oto nagłówek LLVM.
(wiele można szybko odkryć
grep -r /usr/include/*`
)Jedną rzeczą, która wyskakuje, jest
*
przeciążenie operatora (zwracanie 0 jest o wiele bardziej przyjazne niż segfault ...). Inną rzeczą jest to, że nie wygląda zgodny z przechowywaniem adresu w ogóle . Co, w porównaniu do tego, w jaki sposób sling void * i przekazywanie wyników NULL do normalnych wskaźników jako wartości wartowników, oczywiście zmniejszyłoby czynnik „nigdy nie zapominaj, może to być bomba”.źródło
Wartość NULL nie musi być równa 0. Jeśli używasz zawsze wartości NULL, a nigdy 0, NULL może mieć dowolną wartość. Zakładając, że programujesz mikrokontroler von Neumana z płaską pamięcią, który ma swoje przerywacze na poziomie 0. Jeśli NULL wynosi 0 i coś pisze pod wskaźnikiem NULL, mikrokontroler ulega awarii. Jeśli NULL to powiedzmy 1024, a przy 1024 jest zmienna zarezerwowana, zapis nie spowoduje jej awarii i możesz wykryć przypisania wskaźnika NULL z wnętrza programu. Jest to bezcelowe na komputerach PC, ale w przypadku sond kosmicznych, sprzętu wojskowego lub medycznego ważne jest, aby nie upaść.
źródło