聊一聊c++中指针为空的三种写法 ----->NULL, 0, nullptr
看到同事用了一下nullptr.不是很了解这方面东东,找个帖子学习学习 http://www.cppblog.com/airtrack/archive/2012/09/16/190828.aspx
NULL:
NULL是c语言的东西,定义处: #define NULL ((void *)0)
我们可以写 int* i = NULL, foo_t* pObj = NULL.
NULL实际上是一个void *的指针,然后吧void *指针赋值给int *和foo_t *的时候,会隐式转换成相应的类型。而如果换做一个C++编译器来编译的话是要出错的,因为C++是强类型的,void *是不能隐式转换成其他指针类型的,所以通常情况下,编译器提供的头文件会这样定义NULL:
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
C++ 的0
因为C++中不能将void *类型的指针隐式转换成其他指针类型,而又为了解决空指针的问题,所以C++中引入0来表示空指针,这样就有了类似上面的代码来定义NULL。实际上C++的书都会推荐说C++中更习惯使用0来表示空指针而不是NULL,尽管NULL在C++编译器下就是0。为什么C++的书都推荐使用0而不是NULL来表示空指针呢?我们看一个例子:
//foo.h
void bar(type1 a, type2* b); //foo.h中的bar函数在a.cpp和b.cpp中都被调用了. //a.cpp
.....
bar(a,b);
..... //b.cpp
.....
bar(a, );
..... //现在,上面的代码都能编译运行.但是突然某天我们要功能扩展,需要对bar函数扩展,我们使用了重载 foo.h变成如下:
void bar(sometype1 a, sometype2 *b);
void bar(sometype1 a, int i); //这个时候就危险了.因为a.cpp和b.cpp中的调用代码这个时候就不能按照期望的运行了。但是我们很快就会发现b.cpp中的0是整数,也就是在overload resolution的时候,我们知道它调用的是void bar(sometype1 a, int i)这个重载函数,于是我们可以做出如下修改让代码按照期望运行: bar(a, static_cast<type2 *>()); //我知道,如果我们一开始就有bar的这两个重载函数的话,我们会在一开始就想办法避免这个问题(不使用重载)或者我们写出正确的调用代码,然而后面的这个重载函数或许是我们几个月或者很长一段时间后加上的话,那我们出错的可能性就会加大了不少。貌似我们现在说道的这些跟C++通常使用0来表示空指针没什么关系,好吧,假设我们的调用代码是这样的:
//foo.h
void bar(type1 a, type2 *b); //a.cpp
......
bar(a, b);
......
//b.cpp
.....
bar(a,NULL);
..... //当bar的重载函数在后面加上来了之后,我们会发现出错了,但是出错的时候,我们找到b.cpp中的调用代码也很快可能忽略过去了,因为我们用的是NULL空指针啊,应该是调用的void bar(type1 a, type2 *b)这个重载函数啊。实际上NULL在C++中就是0,写NULL这个反而会让你没那么警觉,因为NULL不够“明显”,而这里如果是使用0来表示空指针,那就会够“明显”,因为0是空指针,它更是一个整形常量。 在C++中,使用0来做为空指针会比使用NULL来做空指针会让你更加警觉。
C++ 11的nullptr
//虽然上面我们说明了0比NULL可以让我们更加警觉,但是我们并没有避免这个问题。这个时候C++ 11的nullptr就很好的解决了这个问题,我们在C++ 11中使用nullptr来表示空指针,这样最早的代码是这样的 //foo.h void bar(type1 a, type2* b);
-------------------------------------------
7 | //a.cpp | //b.cpp |
| .... | ....... |
| bar(a,b); | bar(a, nullptr);|
| ....... | ........ |
---------------------------------------------
在我们后来把bar的重载加上了之后,代码是这样: //foo.h
void bar(type1 a, type2 *b);
void bar(type1 a, int i); //a.cpp //b.cpp
.... .....
bar(a,b); bar(a,nullptr);
.... ...... //这时候,我们的代码还是能够如期的正确运行. //在没有C++11的nullptr的时候,我们应该怎么解决避免这个问题呢?我们可以自己实现一个nullptr const
class nullptr_t
{
public:
template<class T>
inline operator T*() const
{ return ; } template<class C, class T>
inline operator T C::*() const
{ return ; } private:
void operator&() const;
} nullptr = {};

和小伙伴都惊了个呆了啊...上面自己实现的nullptr_t类完全看不懂的样子,得一句一句的分析分析啊...
const //参见下文解释1
class nullptr_t
{
public:
template<class T>
inline operator T* () const //参见下文解释2
{
return ;
} template<class C, class T>
inline operator T C::*() const //参见下文解释3
{
return ;
} private:
void operator& () const; //参见下文解释4 }nullptr = {}; //参见下文解释5
解释1 : 在类前面的const是什么!!
//类前面的const 是修饰 类后面定义的对象的. const
class A
{
public:
int i;
}a,b; //等价于
class A
{
public:
int i;
}; const A a;
const A b;
解释2 : 这个模板函数是什么?
template <class T>
inline operator T* () const
{
return ;
} //上面的模板函数是在重载 类型转换运算符 (跟 重载 operator = 一样都是在重载运算符).
//我们知道重载运算符是要带返回值的,例如下面的类重载等号 是要返回A&的
class A
{
public:
A& operater = (const A& Obj)
{
i = Obj.i;
j = Obj.j;
}
private:
int i;
int j;
};
//但是呢.重载 类型转换运算符比较特殊. (c++ primer,可以查“用户自定义类型转换符”找到讲解) 规定为:转换函数不能写返回类型(规定的),返回的类型就是 operator 后面跟的类型
所以:
inline operator T* () const
---------------------------------------------
这是一个类型转换函数,把A类型(这里是return 0 的 0的类型)转换成 T*的类型
inline 表示内联函数,写不写无所谓。
operator 代表重载某种操作 operator T* () 就是重载类型转换
const 表示 成员变量是只读,不能改。 return _data; // 有返回值,返回值类型是operator后面的T*类型。
解释3 : 这个模板函数又是什么???
解释4: 只写函数声明,不写函数实现真的可以么??
事实证明是可以的,只有没人调用这个函数是可以编译运行成功的..但是如果有人调用的话编译就会报错了.


解释5: 类对象 = {} 中的 ={}是什么??
= {} 是c++11的语法,代表给这个变量初始化



聊一聊c++中指针为空的三种写法 ----->NULL, 0, nullptr的更多相关文章
- Android平台中实现对XML的三种解析方式
本文介绍在Android平台中实现对XML的三种解析方式. XML在各种开发中都广泛应用,Android也不例外.作为承载数据的一个重要角色,如何读写XML成为Android开发中一项重要的技能. 在 ...
- Java中获取键盘输入值的三种方法
Java中获取键盘输入值的三种方法 Java程序开发过程中,需要从键盘获取输入值是常有的事,但Java它偏偏就没有像c语言给我们提供的scanf(),C++给我们提供的cin()获取键盘输入值 ...
- MVC3中,在control里面三种Html代码输出形式
MVC3中,在control里面三种Html代码输出形式:ViewData["msg"] = "<br /> Title <br />" ...
- jquery 在页面中三种写法
jQuery 分 2 个系列版本 1.x 与 2.x,主要的区别在于 2.x 不再兼容 IE6.7.8浏览器,这样做的目的是为了兼容移动端开发.由于减少了一些代码,使得该版本比 jQuery 1.x ...
- Shell脚本中字符串判空:使用-z 字符串长度为0时,为真,-n字符串长度不为0,为真。这两个都不靠谱【转】
最近发现使用 -z 和 -n 来判断字符串判空,或不空时,很不靠谱. 使用下面的方法最可靠: if [ "x${value}" == "x" ] ...
- 在Tomcat中部署web项目的三种方式
搬瓦工搭建SS教程 SSR免费节点:http://www.xiaokeli.me 在这里介绍在Tomcat中部署web项目的三种方式: 1.部署解包的webapp目录 2.打包的war文件 3.Man ...
- 【转载】取得系统中网卡MAC地址的三种方法
From:http://blog.csdn.net/zhangting1987/article/details/2732135 网卡地址这个概念有点混淆不清.因为实际上有两个地址,mac地址和物理地址 ...
- Tomcat中部署web应用的三种方式
Tomcat中部署web应用的三种方式(静态部署) 第一种,针对war或解压后的war,最为常用的是直接操作webapp目录,将完整的war包或者web应用直接放到webapp目录下.使用 ...
- spring中创建bean对象的三种方式以及作用范围
时间:2020/02/02 一.在spring的xml配置文件中创建bean对象的三种方式: 1.使用默认构造函数创建.在spring的配置文件中使用bean标签,配以id和class属性之后,且没有 ...
随机推荐
- 如何屏蔽Button setClickable与setEnabled
今天想让按键暂时无效,满足一定条件下才可以被用户按到.最初以为是setClickable,谁知搞错了.请看手册: 复制代码代码如下: public void setClickable (boolean ...
- 关于PreferenceActivity的使用和一些问题的解决(自己定义Title和取值)
android的Setting往往用PreferenceActivity来写的 我们在建立layout文件: <PreferenceScreen xmlns:android="http ...
- RFC 文档(中文与英文)
http://man.chinaunix.net/develop/rfc/default.htm https://www.rfc-editor.org/retrieve/ http://www.iet ...
- Qt 国际化之二:多国语界面动态切换的实现
第一步在你的pro里面加入 TRANSLATIONS = myexec_zh.ts (根据对应的ts文件修改)第二步用lupdate 操作pro 将要翻译的提取到ts文件 命令是 lupdate my ...
- maven 学习1 -安装maven 并执行编译命令
一.maven 下载与安装(安装好jdk的前提下) 1.下载地址:http://maven.apache.org/download.cgi (选择最新的zip版本),下载完毕后解压 2.安装:系统p ...
- ASP.NET性能优化之分布式Session
如果我们正在使用Session,那么构建高性能可扩展的ASP.NET网站,就必须解决分布式Session的架构,因为单服务器的SESSION处理能力会很快出现性能瓶颈,这类问题也被称之为Session ...
- Google Map API v2 (三)----- 地图上添加标记(Marker),标记info窗口,即指定经纬度获取地址字符串
接上篇 http://www.cnblogs.com/inkheart0124/p/3536322.html 1,在地图上打个标记 private MarkerOptions mMarkOption; ...
- Silverlight OOB 程序自动更新
Silverlight OOB 程序 提供了非常方便的自动更新功能! 要让 Silverlight OOB 安装到客户端电脑后实现自动更新,必须实现以下两个条件: 一.为 程序的 xap 文件进行签 ...
- .net数据传递的格式
1 Object 返回数据库查询后的单个值 public object LoadBusinessScopeById(int id) { string sql = "select [name] ...
- 在picture library中取某一图片的大图、小图
public static string GetPicThumbnail(SPFile file, string type) { string thumbnail = "" ...