跳到正文

Wiki

nullptr

約 4 分鐘閱讀

本文由簡體中文內容確定性轉換,並受版本化術語表保護。

1.本節解決什麼問題

在傳統 C/C++ 中,用 NULL0 表示空指標。但這有問題:

  • NULL 實際上就是 0(在 C++ 中定義為 #define NULL 0),它會被當作整數來處理,導致型別不匹配。
  • 在某些過載函式中,NULL 可能匹配到整數版本的函式而非指標版本。

nullptr 是真正的"空指標型別",解決了這些型別安全問題。

2.這個特性是什麼

nullptr 是 C++11 引入的關鍵字,型別是 std::nullptr_t,可以隱式轉換為任何型別的指標,但不能隱式轉換為整數型別。這讓編譯器能區分"空指標"和"整數 0"。

3.C++ 標準版本

C++11

4.需要的標頭檔案

不需要額外標頭檔案。nullptr 是語言關鍵字。如果需要使用 std::nullptr_t 型別,需要 #include <cstddef>

5.基本語法

int* p = nullptr;           // 空 int 指针
double* d = nullptr;        // 空 double 指针
std::shared_ptr<int> sp = nullptr;  // 空智能指针

// 判断空指针
if (p == nullptr) { ... }
if (!p) { ... }             // 等价写法
if (p) { ... }              // p 非空

6.常用用法

用法 說明
int* p = nullptr; 初始化空指標
p == nullptr 判斷是否為 nullptr
if (ptr) 判斷非空
func(nullptr) 傳遞空指標引數
return nullptr; 返回空指標

7.nullptr 和其他"空"的區別

nullptr 只表示"沒有指向任何物件的指標"。它不是整數 0,也不是空字串,更不是容器為空。

表達含義 推薦寫法 不要混用
空指標 T* p = nullptr; 不要寫 NULL0
空智慧指標 std::shared_ptr<T> p = nullptr; 不要手動 new/delete
空字串 std::string s;"" 不要用 nullptr
可能沒有返回值 std::optional<T> 不要硬塞一個"特殊值"
容器為空 v.empty() 不要和指標是否為空混淆

8.示例程式碼

8.1.示例 1:nullptr 基本使用

#include <iostream>
#include <cstddef>

int main()
{
    int* p1 = nullptr;    // 空指针初始化
    int* p2 = 0;           // 旧写法(用 0 赋给指针)
    int* p3 = NULL;        // 旧写法(NULL 实际上还是 0)

    // 三种写法在简单的场景下等价
    std::cout << "p1 = " << p1 << "\n";
    std::cout << "p2 = " << p2 << "\n";
    std::cout << "p3 = " << p3 << "\n";

    // 判断空指针
    if (p1 == nullptr)
    {
        std::cout << "p1 is null\n";
    }

    return 0;
}

執行結果

p1 = 0
p2 = 0
p3 = 0
p1 is null

8.2.示例 2:在示例 1 基礎上,nullptr 解決過載歧義

#include <iostream>

// 两个重载函数
void process(int n)
{
    std::cout << "process(int): " << n << "\n";
}

void process(int* p)
{
    if (p == nullptr)
    {
        std::cout << "process(int*): nullptr\n";
    }
    else
    {
        std::cout << "process(int*): " << *p << "\n";
    }
}

int main()
{
    process(42);        // 调用 process(int)
    process(nullptr);   // 调用 process(int*),明确是指针版本


    process(0);      // 调用 process(int)
    // process(NULL);   // ❌ 有歧义!编译器不知道调哪个

    int* p = nullptr;
    process(p);         // 调用 process(int*)

    return 0;
}

執行結果

process(int): 42
process(int*): nullptr
process(int): 0
process(int*): nullptr

8.3.示例 3:在示例 2 基礎上,nullptr 在模板和智慧指標中的應用

#include <iostream>
#include <memory>

template <typename T>
void check_ptr(T* ptr)
{
    if (ptr == nullptr)
    {
        std::cout << "pointer is null\n";
    }
    else
    {
        std::cout << "pointer value = " << *ptr << "\n";
    }
}

int main()
{
    // 原始指针初始化为空
    int* rp = nullptr;
    check_ptr(rp);

    // 智能指针初始化为空
    std::shared_ptr<int> sp = nullptr;
    // 智能指针也可直接用 bool 判断
    if (!sp)
    {
        std::cout << "shared_ptr is null\n";
    }

    // 创建后赋值
    sp = std::make_shared<int>(100);
    if (sp)
    {
        std::cout << "shared_ptr value = " << *sp << "\n";
    }

    return 0;
}

執行結果

pointer is null
shared_ptr is null
shared_ptr value = 100

8.4.示例 4:nullptr 不是魔法——多個指標過載仍需指定型別

#include <iostream>

void reset(int* p)
{
    std::cout << "reset int pointer\n";
    if (p != nullptr)
    {
        *p = 0;
    }
}

void reset(double* p)
{
    std::cout << "reset double pointer\n";
    if (p != nullptr)
    {
        *p = 0.0;
    }
}

int main()
{
    int count = 42;
    double ratio = 3.14;

    reset(&count);   // 明确调用 int* 版本
    reset(&ratio);   // 明确调用 double* 版本

    int* count_ptr = nullptr;
    reset(count_ptr); // 仍然明确是 int* 版本

    // reset(nullptr); // ❌ 编译错误:nullptr 可以转成 int*,也可以转成 double*

    std::cout << "count = " << count << "\n";
    std::cout << "ratio = " << ratio << "\n";

    return 0;
}

執行結果

reset int pointer
reset double pointer
reset int pointer
count = 0
ratio = 0

9.執行結果

見上方每個示例的"執行結果"。

10.示例中的關鍵語法解釋

示例 講了什麼 新出現的語法 為什麼這樣寫 注意事項
示例 1 nullptr 基本用法 nullptrp == nullptr 初始化指標為"空",型別安全 nullptr 的型別是 std::nullptr_t
示例 2 解決過載歧義 過載函式中 nullptr 匹配指標版本 NULL 不能區分整數和指標過載,nullptr 可以 這就是 nullptr 替代 NULL 的核心原因
示例 3 模板和智慧指標 shared_ptr = nullptrif(sp) 智慧指標也能用 nullptr 初始化 智慧指標的 bool 轉換等價於 != nullptr
示例 4 多個指標過載 int*double*nullptr nullptr 能排除整數過載,但不能替你選擇具體指標型別 多個指標過載時先把 nullptr 放進明確型別的指標變數

11.常見錯誤

錯誤 1:以為 nullptr 可以匹配任意一個指標過載

void f(int*);
void f(double*);

f(nullptr);  // ❌ 编译错误:两个指针版本都能匹配

正確做法:先明確型別:int* p = nullptr; f(p);

錯誤 2:把 nullptr 賦給整數

int n = nullptr;  // ❌ 编译错误!nullptr 不能隐式转换为整数

正確做法:整型用 0,指標用 nullptr。

錯誤 3:用 NULL 做指標判斷

if (p == NULL) { ... }  // 能工作,但不是最佳实践

正確做法:用 if (p == nullptr) 或直接 if (!p)

錯誤 4:把 nullptr 當布林值用在非預期的地方

std::string s = nullptr;  // ❌ C++23 起直接禁止;旧标准中也是未定义行为

正確做法:空字串用 std::string s;std::string s = "";

12.使用建議

  1. 永遠用 nullptr 而不是 NULL0:這是現代 C++ 的規則,避免型別歧義。
  2. 智慧指標也可用 nullptr:如 std::shared_ptr<T> p = nullptr;
  3. if (ptr) 檢查非空:等價於 if (ptr != nullptr),更簡潔。
  4. 不要給非指標型別賦 nullptr:整數、字串等不要用 nullptr 初始化。

13.小結

  • nullptr 是型別安全的空指標常量,替代 NULL0
  • nullptr 的型別是 std::nullptr_t,只能隱式轉為指標型別。
  • 在過載函式中,nullptr 可以正確匹配到指標版本,NULL 不行。
  • 現代 C++ 中永遠使用 nullptr