C++ NULL與nullptr的區別

C與C++中空指針的區別

在C裏面,因爲到處都要使用指針,因此致使NULL遍及各地。咱們先來看C99是怎麼定義NULL的:ios

NULL can be defined as any null pointer constant. Thus existing code can retain definitions of NULL as 0 or 0L, but an implementation may also choose to define it as (void*)0. This latter form of definition is convenient on architectures where sizeof(void*) does not equal the size of any integer type. It has never been wise to use NULL in place of an arbitrary pointer as a function argument, however, since pointers to different types need not be the same size. The library avoids this problem by providing special macros for the arguments to signal, the one library function that might see a null function pointer.函數

可見,在C99裏面,NULL能夠被定義爲0或者0L(32位和64位的區別),或者直接就是由0或者0L轉成的成void*。
 
接下來咱們來看下C++ 14(N4296)中所定義的null pointer。

A null pointer constant is an integer literal (2.13.2) with value zero or a prvalue of type std::nullptr_t.ui

 

A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of object pointer or function pointer type. Such a conversion is called a null pointer conversion. Two null pointer values of the same type shall compare equal. The conversion of a null pointer constant to a pointer to cv-qualified type is a single conversion, and not the sequence of a pointer conversion followed by a qualification conversion (4.4). A null pointer constant of integral type can be converted to a prvalue of type std::nullptr_t. [ Note: The resulting prvalue is not a null pointer value. end note ]this

 

A prvalue of type pointer to cv T,」 where T is an object type, can be converted to a prvalue of type pointer to cv void」. The result of converting a non-null pointer value of a pointer to object type to a pointer to cv void represents the address of the same byte in memory as the original pointer value. The null pointer value is converted to the null pointer value of the destination type.spa

 

A prvalue of type pointer to cv D」, where D is a class type, can be converted to a prvalue of type pointer to cv B」, where B is a base class (Clause 10) of D. If B is an inaccessible (Clause 11) or ambiguous (10.2base class of D, a program that necessitates this conversion is ill-formed. The result of the conversion is a pointer to the base class subobject of the derived class object. The null pointer value is converted to the null pointer value of the destination type.指針

 
第一句話就代表了,在C++中,一個指向空的指針要麼是一個字面值整形,要麼是一個std::nullptr_t
 
咱們再來看VS 2015 中所定義的NULL,就是一個0
#ifndef NULL #ifdef __cplusplus #define NULL 0
    #else
        #define NULL ((void *)0)
    #endif
#endif
 
用nullptr解決C++中NULL所不能解決的問題

前面咱們說了,C++中的NULL,其實就是一個0,這會致使不少問題,好比咱們能夠寫一個函數重載:
#include <iostream> #include <algorithm> #include <memory>
void fun(int) { std::cout << "fuck1" << std::endl; } void fun(void *) { std::cout << "fuck2" << std::endl; } int main(int argc, char *argv[]) { fun(NULL); system("pause"); return 0; }
 
通常來講,咱們傳進去一個NULL,通常想的是要傳一個指針,但是在上面的程序中,咱們卻調用的是int的版本。
 
可是當咱們傳的是nullptr時:
int main(int argc, char *argv[]) { fun(nullptr); system("pause"); return 0; }
 
這個時候調用的是第二個版本了,符合咱們的設想,這是由於C++規定nullptr能夠轉爲指針類型。並且是cv void *
 
再來一個例子,也就是咱們最多見的模板匹配問題了:
struct Fuck { Fuck(char *){ } }; int main(int argc, char *argv[]) { auto p = std::make_shared<Fuck>(NULL); throwing(); system("pause"); return 0; }
 
這個代碼會報錯,至於爲何,咱們先來分析一下make_shared的模板:
template<class _Ty, class... _Types> inline shared_ptr<_Ty> make_shared(_Types&&... _Args) { // make a shared_ptr
    _Ref_count_obj<_Ty> *_Rx =
        new _Ref_count_obj<_Ty>(_STD forward<_Types>(_Args)...); shared_ptr<_Ty> _Ret; _Ret._Resetp0(_Rx->_Getptr(), _Rx); return (_Ret); } // TEMPLATE CLASS _Ref_count_obj
template<class _Ty>
    class _Ref_count_obj : public _Ref_count_base { // handle reference counting for object in control block, no allocator
public: template<class... _Types> _Ref_count_obj(_Types&&... _Args) : _Ref_count_base() { // construct from argument list
        ::new ((void *)&_Storage) _Ty(_STD forward<_Types>(_Args)...); } _Ty *_Getptr() const { // get pointer
        return ((_Ty *)&_Storage); } private: virtual void _Destroy() _NOEXCEPT { // destroy managed resource
        _Getptr()->~_Ty(); } virtual void _Delete_this() _NOEXCEPT { // destroy self
        delete this; } typename aligned_union<1, _Ty>::type _Storage; };
 
這裏多說幾句,make_shared的操做是先給_Ref_count_obj<_Ty>類型分配一塊內存,而後再placement new,回想一下咱們日常使用shared_ptr的時候,都是shared_ptr<T> foo(new T(arg...))這樣用的,可是其實用make_shared建立shared_ptr的方法更爲高效,由於咱們從模板中能夠看到shared_ptr的佔用空間實際上是要比T要大的(爲了保存引用計數的東西)。若是咱們使用shared_ptr<T> foo(new T(arg...))來構造shared_ptr,那麼要先給T分配內存並構造T,而後在分配ref_count的內存,可是若是使用make_shared,那麼就會直接給T和ref_count一塊兒分配內存,而後再經過C++11的完美轉發把T的構造函數傳給make_shared。
 
好如今回到咱們這篇博客的主題,爲何傳一個NULL會報錯呢?這是由於因爲C++的NULL就是一個字面值常量0,因此傳進去時,會被forward推斷成int &&,int &&與char *固然不是一個東西,就會報錯。
 
這個時候咱們就必須使用nullptr了,nullptr能夠轉換成void *,而後再隱式轉換成char *
auto p = std::make_shared<Fuck>(nullptr);
相關文章
相關標籤/搜索