聊一聊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属性之后,且没有 ...
随机推荐
- linq 学习笔记(一)
First: 找到符合条件的第一记录,就返回了,不管后面还有多少数据. Single: 先将记录都梳理一次,再找到符合要求的唯一记录. Single():操作一个集合,同时强要求只有一个对象匹配, ...
- TimeSpan XML序列化
/// <summary> /// 刷新时间 默认为1秒 /// </summary> /// <value>The refresh time.</value ...
- 非常实用的PHP常用函数汇总
这篇文章主要介绍了非常实用的PHP常用函数,汇总了加密解密.字符串操作.文件操作.SQL注入等函数的实例与用法说明,在PHP项目开发中非常具有实用价值,需要的朋友可以参考下 本文实例总结了一些在php ...
- Xilinx ISE14.7 安装教程(转)
文章来源http://blog.chinaaet.com/crazybird/p/39693 作者:crazybird **************************************** ...
- socket编程原理
socket编程原理 1.问题的引入 1) 普通的I/O操作过程: UNIX系统的I/O命令集,是从Maltics和早期系统中的命令演变出来的,其模式为打开一读/写一关闭(open-write-rea ...
- C# 分页
#region 分页 /// <summary> /// 分页 /// </summary> /// <param name="page">当前 ...
- Promise的用简要使用方式
Promise用法 在项目中用到异步请求ajax,想到用promise来解决,之前用过但是已经很久了,还是忘了一些,重新熟悉了一下整理一份简要文档. Promise,就是一个对象,用来传递异步操作的消 ...
- Linq 中的distinct去重
Linq的Distinct和T-Sql的distinct一样,可以将重复的结果集去重注意: 1 distinct去重记录要求每个字段都重复时,才算重复对象,这与sql一样2 distinct语句可以和 ...
- Struts2多文件上传
第一步:首先创建一个多文件上传的页面 <html> <head> <meta http-equiv="Content-Type" content=&q ...
- Java 中判断char 是否为空格 和空
//判断是否char是否为空import java.util.*; public class test{ public static void main(String[] args){ String ...