第 18.2 節
nullptr
0瀏覽次數0訪問次數--跳出率--平均停留
本節解決什麼問題
在傳統 C/C++ 中,用 NULL 或 0 表示空指針。但這有問題:
NULL實際上就是0(在 C++ 中定義爲#define NULL 0),它會被當作整數來處理,導致類型不匹配。- 在某些重載函數中,
NULL可能匹配到整數版本的函數而非指針版本。
nullptr 是真正的"空指針類型",解決了這些類型安全問題。
這個特性是什麼
nullptr 是 C++11 引入的關鍵字,類型是 std::nullptr_t,可以隱式轉換爲任何類型的指針,但不能隱式轉換爲整數類型。這讓編譯器能區分"空指針"和"整數 0"。
C++ 標準版本
C++11
需要的頭文件
不需要額外頭文件。nullptr 是語言關鍵字。如果需要使用 std::nullptr_t 類型,需要 #include <cstddef>。
基本語法
int* p = nullptr; // 空 int 指针
double* d = nullptr; // 空 double 指针
std::shared_ptr<int> sp = nullptr; // 空智能指针
// 判断空指针
if (p == nullptr) { ... }
if (!p) { ... } // 等价写法
if (p) { ... } // p 非空
常用用法
| 用法 | 說明 |
|---|---|
int* p = nullptr; | 初始化空指針 |
p == nullptr | 判斷是否爲 nullptr |
if (ptr) | 判斷非空 |
func(nullptr) | 傳遞空指針參數 |
return nullptr; | 返回空指針 |
nullptr 和其他"空"的區別
nullptr 只表示"沒有指向任何對象的指針"。它不是整數 0,也不是空字符串,更不是容器爲空。
| 表達含義 | 推薦寫法 | 不要混用 |
|---|---|---|
| 空指針 | T* p = nullptr; | 不要寫 NULL 或 0 |
| 空智能指針 | std::shared_ptr<T> p = nullptr; | 不要手動 new/delete |
| 空字符串 | std::string s; 或 "" | 不要用 nullptr |
| 可能沒有返回值 | std::optional<T> | 不要硬塞一個"特殊值" |
| 容器爲空 | v.empty() | 不要和指針是否爲空混淆 |
示例代碼
示例 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
示例 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(NULL); // ❌ 有歧义!编译器不知道调哪个
// process(0); // ❌ 同样的问题!0 匹配 process(int)
int* p = nullptr;
process(p); // 调用 process(int*)
return 0;
}
運行結果:
process(int): 42
process(int*): nullptr
process(int*): nullptr
示例 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
示例 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
運行結果
見上方每個示例的"運行結果"。
示例中的關鍵語法解釋
| 示例 | 講了什麼 | 新出現的語法 | 爲什麼這樣寫 | 注意事項 |
|---|---|---|---|---|
| 示例 1 | nullptr 基本用法 | nullptr、p == nullptr | 初始化指針爲"空",類型安全 | nullptr 的類型是 std::nullptr_t |
| 示例 2 | 解決重載歧義 | 重載函數中 nullptr 匹配指針版本 | NULL 不能區分整數和指針重載,nullptr 可以 | 這就是 nullptr 替代 NULL 的核心原因 |
| 示例 3 | 模板和智能指針 | shared_ptr = nullptr、if(sp) | 智能指針也能用 nullptr 初始化 | 智能指針的 bool 轉換等價於 != nullptr |
| 示例 4 | 多個指針重載 | int*、double*、nullptr | nullptr 能排除整數重載,但不能替你選擇具體指針類型 | 多個指針重載時先把 nullptr 放進明確類型的指針變量 |
常見錯誤
錯誤 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 = "";。
使用建議
- 永遠用
nullptr而不是NULL或0:這是現代 C++ 的規則,避免類型歧義。 - 智能指針也可用
nullptr:如std::shared_ptr<T> p = nullptr;。 - 用
if (ptr)檢查非空:等價於if (ptr != nullptr),更簡潔。 - 不要給非指針類型賦 nullptr:整數、字符串等不要用 nullptr 初始化。
小結
nullptr是類型安全的空指針常量,替代NULL和0。nullptr的類型是std::nullptr_t,只能隱式轉爲指針類型。- 在重載函數中,
nullptr可以正確匹配到指針版本,NULL不行。 - 現代 C++ 中永遠使用
nullptr。