C++11 中, nullptr 是空指針,可用來給(指向任意對象類型的)指針進行賦值
廣義整型 (integral types) = char, short, int, long, long longnd and their unsigned counterparts, and bool, wchar_t, char16_t, and char32_
1 調用重載函數
0 在 C++ 中,被首先視為 int 型。NULL 在 C++ 中,首先視為廣義整型 (integral types),至於具體是 int,long 或是其它,要視相應的情況而定
下面三個函數,因為形參類型的不同,構成重載函數。如果各傳遞三個不同的實參,來選擇調用哪個函數,則會出現如下問題:
// three overloads of f void f(int); void f(bool); void f(void*); f(0); // calls f(int), not f(void*) f(NULL); // might not compile, but typically calls f(int). Never calls f(void*) f(nullptr); // calls f(void*) overload
1) C++ 視 0 首先為 int 型,因此,調用 f(0) 即調用 f(int)
2) NULL 的情況復雜些,但 C++ 仍然視其首先為廣義整型(integral tyoes)。假如 NULL 被定義為普通的 0,則調用 f(int)。
如果 NULL 被定義成 0L,則 long -> int, long -> bool, 0L -> void*, 這三種情況都是合法的,此時,編譯器會報錯
3) 使用 nullptr,則不會有重載函數調用模糊的問題
- nullptr 不屬於廣義整型,也不是普通意義上的指針
- nullptr 的實際類型是 std::nullptr_t,它能夠隱式的轉換成所有的原始指針類型,故可將其視為一個指向所有類型的指針
2 代碼清晰
使用 nullptr 來代替 0 或 NULL,能夠顯著提高代碼的清晰度,如下例所示:
auto result = findRecord( /* arguments */ ); if (result == 0) { ... } auto result = findRecord( /* arguments */ ); if (result == nullptr) { ... }
使用 0 與 result 作比較,則第一眼很難確定 findRecord 的返回值類型,因為可能是廣義整型,也可能是指針類型
而使用 nullptr,卻可以清楚地知道 findRecord 的返回值,必定是一個指針類型
3 模板函數
當程序中涉及模板(template)時,使用 nullptr 的好處更加明顯,如下所示:
// call these only when the appropriate mutex is locked int f1(std::shared_ptr<Widget> spw); double f2(std::unique_ptr<Widget> upw); bool f3(Widget* pw); // calling code that wants to pass null pointers std::mutex f1m, f2m, f3m; // mutexes for f1, f2, and f3 using MuxGuard = std::lock_guard<std::mutex>; ... { MuxGuard g(f1m); // lock mutex for f1 auto result = f1(0); // pass 0 as null ptr to f1 } // unlock mutex ... { MuxGuard g(f2m); // lock mutex for f2 auto result = f2(NULL); // pass NULL as null ptr to f2 } // unlock mutex ... { MuxGuard g(f3m); // lock mutex for f3 auto result = f3(nullptr); // pass nullptr as null ptr to f3 } // unlock mutex
lock mutex -> call function -> unlock mutex,這個模式在程序中重復了三次,要想避免這種重復,可用一個模板函數代替之
template<typename FuncType, typename MuxType, typename PtrType> auto lockAndCall(FuncType func, MuxType& mutex, PtrType ptr) -> decltype(func(ptr)) // C++11 { MuxGuard g(mutex); return func(ptr); }
最後,調用該模板函數
auto result1 = lockAndCall(f1, f1m, 0); // error! ... auto result2 = lockAndCall(f2, f2m, NULL); // error! ... auto result3 = lockAndCall(f3, f3m, nullptr); // fine
當 0 作為實參傳遞給 lockAndCall 函數時,其被 C++ 推斷為 int 型,這與 f1 所期望的 std::shared_ptr<Widget> 型參數明顯不符,因此出現報錯
同理,NULL 與 f2 期望的 std::unique_ptr<Widget> 型參數也不符合
nullptr,作為 ptr 傳給 f1 或 f2 時,被推斷為 std::nullptr_t ; 作為 ptr 傳給 f3 時,std::nullptr_t 會隱式的轉換成 Widget*,確保了參數類型的一致
小結:
1) prefer nullptr to 0 and NULL
2) avoid overloading on integral and pointer types
參考資料
<Effective Modern C++> Item 8
<C++ Programming Language> "integral types"