C++11 之 nullptr


  C++11 中,nullptr 是空指針,可用來給 (指向任意對象類型的) 指針賦值

  廣義整型 (integral types) = char, short, int, long, long long and their unsigned counterparts, and bool, wchar_t, char16_t, and char32_

1  調用重載函數

  C++ 中,0 首先被視為 int 型,而 NULL 首先被視為廣義整型 (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++ 首先視其為廣義整型。假如 NULL 被定義為普通的 0,則調用 f(int);

   如果 NULL 被定義成 0L,則 long -> int, long -> bool, 0L -> void*, 這三種情況都是合法的,此時,編譯器會報錯

3) 使用 nullptr,則不會有重載函數調用模糊的問題 

    - nullptr 不屬於廣義整型,也不是普通意義上的指針。

    - nullptr 的實際類型是 std::nullptr_t,它能夠隱式的轉換成所有的原始指針類型,故可將其視為一個可指向所有類型的指針。

 

2  代碼清晰

   使用 nullptr 代替 0 或 NULL,能顯著提高代碼的清晰度,尤其是和  auto 連用時,如下:

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"

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM