0 和 NULL
0 的类型:在 C++ 中,字面值 0 默认是一个 int 类型,而不是指针类型。只有在上下文明确需要指针时,编译器才会不情愿地将其解释为指针。
NULL 的实现:NULL 通常被定义为 0 或 0L(long 类型的 0)。尽管 NULL 可以被定义为其他整型类型,但它的主要问题是它本质上仍然是一个整型,而不是指针类型。
在 C++98 中,如果同时重载了指针和整型的函数,传递 0 或 NULL 会调用整型版本的函数,而不是指针版本的函数。这是因为 0 和 NULL 被解析为整型,而不是指针。
nullptr 的优势
nullptr 的类型:nullptr 是 C++11 引入的一个关键字,其类型是 std::nullptr_t。std::nullptr_t 可以隐式转换为任何指针类型,但它不是整型。使用 nullptr 调用函数时,编译器会正确地选择指针版本的重载函数,而不是整型版本的函数。使用 nullptr 可以使代码更加清晰,尤其是当与 auto 声明的变量一起使用时。例如,if (result == nullptr) 明确表示 result 是一个指针类型。在模板中,传递 0 或 NULL 会导致类型推导出整型,而不是指针类型。这会导致类型错误,因为模板实例化时会尝试将整型传递给期望指针的函数。使用 nullptr 可以避免这些问题,因为 std::nullptr_t 可以隐式转换为任何指针类型,而不会导致类型错误。在 C++11 及以上版本中,优先使用nullptr 而不是 0 或 NULL。尽量避免同时重载指针和整型的函数,以减少潜在的解析问题和代码复杂性。
void f(int);
void f(bool);
void f(void*);
f(0);//调用f(int)
f(NULL);//通常调用f(int),可能有二义性
f(nullptr);//调用 f(void*)
模板示例
#include <memory>
#include <mutex>
template<typename FuncType, typename MuxType, typename PtrType>
auto lockAndCall
(FuncType func, MuxType& mutex, PtrType ptr) -> decltype(func(ptr)) {
std::lock_guard<MuxType> g(mutex);
return func(ptr);
}
template<typename FuncType, typename MuxType, typename PtrType>
decltype(auto) lockAndCall(FuncType func, MuxType& mutex, PtrType ptr) {
std::lock_guard<MuxType> g(mutex);
return func(ptr);
}
int f1(std::shared_ptr<int>);
double f2(std::unique_ptr<int>);
bool f3(int*);
std::mutex m1, m2, m3;
int main() {
auto result1 = lockAndCall(f1, m1, 0); // 错误!
auto result2 = lockAndCall(f2, m2, NULL); // 错误!
auto result3 = lockAndCall(f3, m3, nullptr); // 没问题
}
结论
使用 nullptr 可以避免许多与 0 和 NULL 相关的类型解析问题,使代码更加清晰和健壮。在现代 C++ 编程中,推荐优先使用 nullptr。