我们知道:0是一个int,而不是一个指针。如果C++在一个只有指针才能够使用的上下文中发现它只有一个0,那么它会勉强将0解释成空指针,但那时一种倒退行为。C++的主要方针是0就是一个int,而不是指针。

实际上来说,对于NULL也是一样。关于NULL还有一些不确定因素,因为其实现允许给NULL一个整型而不必是int(比如说long)。这并不常见,但是也无关紧要,因为这里的问题并不是NULL的确切类型是什么,而是0和NULL都不是指针类型。

C++98中,主要的启示就是对指针和整型分别进行重载可能会导致意想不到的结果。传递0或者NULL给这些重载将永远不会调用指针版本的重载函数:

void f(int); // three overloads of f
void f(bool);
void f(void*); f(); // calls f(int), not f(void*)
f(NULL); // might not compile, but typically calls
// f(int). Never calls f(void*)

关于f(NULL)行为的不确定性是NULL的实现类型不确定性的一个反映。如果NULL被定义成,比如说,0L(也就是说是long),那么该调用就很模棱两可,因为long到int的转换,long到bool的转换以及0L到void*的转换被认为一样好。关于该调用的一个有趣的事就是该源代码看起来的含义(”我用NULL(空指针)调用f”)和其实际的含义(”我用NULL(某种整数)调用f”)之间的冲突。这种违法直觉的行为促成了c++98程序员的一个指导方针就是避免重载指针类型和整型。该指导方针在C++11中依然有效,因为尽管我在这里建议大家用nullptr,但是很多开发者很可能还是继续使用0和NULL。

nullptr的优势在于它并不是一个整型。实话实说,它其实也不是一个指针类型,但是你可以认为它是所有类型的指针。nullptr的真实类型是std::nullptr_t,而std::nullptr_t又被定义成nullptr的类型,形成一个极好的环形定义。std::nullptr_t可以隐形转换任何原始指针类型,而正是这才让nullptr用起来像是所有类型的指针。

使用nullptr来调用重载函数f会调用void*版本的重载函数,因为nullptr不能被当做成一个整型来看:

f(nullptr);         //calls f(void*) overload

所以使用nullptr而不是0或者NULL防止调用不合理的重载函数,但是这并不是其仅有的优势。它还可以提高代码的清晰度,尤其当你使用auto变量时。例如,假设你遇到下面这种代码:

auto result = findRecord(/* arguments */);

if(result == ){
...
}

如果你恰好不知道(或者不容易弄清楚)findRecord的返回类型,那么这个result是一个指针类型还是一个整型就可能不太清楚了。毕竟,0对于上面哪一种情况都适用。如果你看到下面的代码:

auto result = findRecord(/* arguments */);

if(result == nullptr){
...
}
 

就没有任何歧义了:result就是一个指针类型

而当涉及到模板时,nullptr更是大显光彩。假设你有一些函数,只有当合适的mutex被锁定时才应该被调用。每个函数都接受一个不同类型的指针:

int f1(std::shared_ptr<Widget> spw);        // call these only when
double f2(std::unique_ptr<Widget> upw); // the appropriate
bool f3(Widget* pw); // mutex is locked

想要传递空指针的函数调用写起来可能像下面这样:

std::mutex f1m, f2m, f3m;   //mutexes for f1,f2,f3

using MuxGuard = std::lock_guard<std::mutex>; //C++11 typedef; See Item 9
... {
MuxGuard g(f1m); //lock mutex for f1
auto result = f1(); //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

该代码中前两个调用没有使用nullptr真是一个悲哀,但是这份代码可以正常工作,有一定价值。然而,调用代码中的重复模式—锁mutex,调用函数,解锁—却更加让人悲伤。这很令人烦。这种类型的代码重复是模板被设计用来避免的事物之一,所以咱们对这种模式使用模板:

template<typename FuncType,
typename MuxType,
typename PtrType>
auto lockAndCall(FuncType func,
MuxType& mutex,
PtrType ptr) -> decltype(func(ptr))
{
MuxGuard g(mutex);
return func(ptr);
}

如果你对于这种函数返回类型(auto…->decltype(func(ptr)))还不熟悉,那么请你参考Item 3,那里解释的很清楚。如果你使用C++14,返回类型还可以被精简成decltype(auto):

template<typename FuncType,
typename MuxType,
typename PtrType>
auto lockAndCall(FuncType func, //C++14
MuxType& mutex,
PtrType ptr)
{
MuxGuard g(mutex);
return func(ptr);
}

基于lockAndCall模板(哪一个版本都行),调用代码可以写成如下:

auto result1 = lockAndCall(f1,f1m,);  //error!
...
auto result2 = lockAndCall(f2,f2m,NULL); //error!
...
auto result3 = lockAndCall(f3,f3m,nullptr); //fine

他们可以这样写,但是正如评论所说的,前两个并不会通过编译。第一个调用的问题在于0被传递给lockAndCall,模板类型推断计算出它的类型。0的类型,过去是,现在也一直都是int,所以在针对这个调用的lockAndCall实例中,其参数ptr的类型就是int。不幸的是,这意味着lockAndCall内部的func调用中,一个int被传递了,而这和f1期待的std::shared_ptr<Widget>参数类型不匹配。传递给lockAndCall的0本来打算是表示空指针的,但是传递进去的实际类型是一个普通的int。尝试给f1传递int当做其std::shared<Widget>参数会导致类型错误。使用0来调用lockAndCall会失败是因为在模板内部,一个int类型被传递给了一个需要std::shared_ptr<Widget>类型的函数里。

对于涉及到NULL的调用的分析和上面基本上一模一样。当NULL被传递给lockAndCall时,对参数ptr推断出的类型是一个整型,而当一个int或者类似于int的类型被传递个期待一个std::unique+ptr<Widget>f2时,就会发生类型错误。

作为对比,使用nullptr调用就不存在问题。当nullptr被传递给lockAndCall时,ptr的类型被推断成std::nullptr_t。当ptr被传递给f3时,有一个隐性转换将std::nullptr_t转换成Widget*类型,因为std::nullptr_t可以隐性转换成任意指针类型。

当你想要表示一个空指针时,使用nullptr而不是0或者NULL的最大一个原因就在于模板类型推断会给0和NULL推断出”错误的”类型(也就是说,它们的真实类型,而不是它们退化的含义,表示一个空指针)。使用nullptr,模板就不会出现什么问题。另外,nullptr不会导致像0和NULL那样的重载决议异常,所以,当你想要表示一个空指针时,使用nullptr,别使用0和NULL.

要点记忆

  • 优先选择nullptr而不是0和NULL
  • 避免对整型和指针类型进行重载

优先选择nullptr而不是0和NULL的更多相关文章

  1. item 8: 比起0和NULL更偏爱nullptr

    本文翻译自modern effective C++,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 博客已经迁移到这里啦 先让我们看一些概念:字面上的0是一个int,不是一个指针.如果C+ ...

  2. C++中 0 与 NULL 与 nullptr之间的关系,nullptr_t 的实现

    C++中 0 与 NULL 与 nullptr之间的关系,nullptr_t 的实现 来源 http://blog.csdn.net/Virtual_Func/article/details/4975 ...

  3. Effective Java 第三版——47. 优先使用Collection而不是Stream来作为方法的返回类型

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  4. 企业开发中选择logback而不是log4j的理由

    不知道看到这篇文章的Java工程师有没有考虑过这个问题:为什么在企业开发中会选择logback来记录日志,而不是log4j呢? 如果你以前没有考虑过这个问题,那么现在如果让你考虑一下,你可能觉的会是因 ...

  5. Effective C# 学习笔记(原则二:为你的常量选择readonly而不是const)

    原则二.为你的常量选择readonly而不是const      Prefer readonly to const 对于常量,C#里面有两个不同的版本:运行时常量(readonly)和编译时常量(co ...

  6. 由于想要实现下载的文件可以进行选择,而不是通过<a>标签写死下载文件的参数,所以一直想要使用JFinal结合ajax实现文件下载,但是ajax实现的文件下载并不能触发浏览器的下载文件弹出框,这里通过模拟表单提交实现同样的效果。

    由于想要实现下载的文件可以进行选择,而不是通过<a>标签写死下载文件的参数,所以一直想要使用JFinal结合ajax实现文件下载(这样的话ajax可以传递不同的参数),但是ajax实现的文 ...

  7. 选择 FreeBSD 而不是 Linux 的技术性原因3

    选择 FreeBSD 而不是 Linux 的技术性原因3 jail FreeBSD Jails 系统是另一个惊人的工程壮举. 在 2000 年 3 月 14 日的 4.0 版本中,FreeBSD 引入 ...

  8. PHP中空字符串介绍0、null、empty和false之间的关系

    PHP中空字符串介绍0.null.empty和false之间的关系 作者: 字体:[增加 减小] 类型:转载 时间:2012-09-25   用PHP开发那么久,PHP中空字符串.0.null.emp ...

  9. php中0,空,null和false之间区别

    $a = 0; $b="0"; $c= ''; $d= null; $e = false; echo "5个变量-原始测试类型"; var_dump($a);/ ...

随机推荐

  1. sqlmap 使用总结

    一直在用sqlmap,一直在浅层的使用方面,所以我想深入了解一下sqlmap. 参考文章: Sqlmap使用教程[个人笔记精华整理]        http://www.vuln.cn/1992 sq ...

  2. linux 权限管理

  3. linux 用户管理命令

  4. 832B Petya and Exam

    题意:给你两个串,第一个串里面的字母都是good 字母, 第二个串是模式串,里面除了字母还有?和*(只有一个) ?可以替换所有good字母, *可以替换所有坏字母和空格(可以是多个坏字母!!!这点卡了 ...

  5. JAVA并行异步编程,线程池+FutureTask

    java 在JDK1.5中引入一个新的并发包java.util.concurrent 该包专门为java处理并发而书写. 在java中熟悉的使用多线程的方式为两种?继续Thread类,实现Runnal ...

  6. html5离线记事本

    离线记事本 这是一个笔记应用,不需要联网,也不需要数据库,可以直接把数据储存在本地.方便易用! ^_^ <!DOCTYPE html>  <html>  <head> ...

  7. Euclideanloss_layer层解析

    这里说一下euclidean_loss_layer.cpp关于该欧式loss层的解析,代码如下: #include <vector> #include "caffe/layers ...

  8. 当linux中的所有指令突然不能使用的时候

    接到同事电话,线上linux系统所有命令执行不了(由于其误操作执行一些命令) 此时可以按以下步骤解决问题: 1.首先导入临时变量(重启虚拟机之后失效),使得所有命令行暂时可以用 直接在命令行执行以下命 ...

  9. async_mongo_helper

    # -*- coding: utf-8 -*- # @Time : 2019/1/7 2:11 PM # @Author : cxa # @File : motortesdt.py # @Softwa ...

  10. JQuery 拾遗

    jquery基本上依赖百度,不熟悉的jquery操作记录于此: 1.判断元素的显示隐藏:$("#XXX").is(':visible'). 2. jquery取所有属性以什么开头 ...