[C.C++] C++11新特性之nullptr

65 0
Honkers 13 小时前 | 显示全部楼层 |阅读模式

        在C++11之前的C++98/03我们使用空都是NULL关键字,后来C++11之后新增了nullptr关键字来表示空。那么有了NULL还要弄个nullptr出来干什么呢?是不是吃太饱了?

        为了来剖析这两者的区别,我们先来看一个什么是野指针;

  1. int serven_1 = 9;
  2. int* serven_p;
  3. int* serven_q = &serven_1;
复制代码

        上面的代码中,serven_p就是野指针,因为定义他的时候没有给他指向的地方,导致他指向了哪些不可用的内存区域:

        所以你定义了指针,就算没有使用他,你也要给个空给他,不然等到内存溢出崩溃的时候,不仅他会哭,你也会被急哭。

        在C++98/03标准中,我们在定义一个指针并且给其赋值为空的时候有两种写法,不仅可以使用NULL,也可以使用0,不信你去试试看:

  1. int* serven_p = NULL;
  2. int* serven_q = 0;
复制代码

        这两种写法完全木有问题,都是OK的。但还是推荐使用第一种写法。毕竟代码编写要规范嘛(这里备注一下:代码编写不规范真的会被打,从现在开始就要养成良好的编程习惯)。

        上面的指针serven_q直接附了0是什么意思呢?有没有联想到大学老师给你上课的时候,反复敲着黑板跟你们说:“咳咳咳~,大家注意了,指针指向的是变量的地址,他本身也是个内存空间,只是这个内存空间存放的是他指向的变量在内存中的地址”。所以呢,这里的serven_q相当于指向了0地址,也就是0x0000 0000这个内存空间。这个0地址呢,在很多操作系统(像Windows、Linux的等等)是不允许用户操作使用的,例如:

  1. #include <QCoreApplication>
  2. #include <iostream>
  3. using namespace std;
  4. int main(int argc, char *argv[])
  5. {
  6.     QCoreApplication a(argc, argv);
  7. int* serven_p = 0;
  8.     // *serven_p = 4;          // 这是不允许的
  9. printf("%p\n", serven_p);
  10.     // printf("%d\n", *serven_p);
  11. return a.exec();
  12. }
复制代码

运行结果:

结果分析:

    serven_p指向的是0地址。

        在上面的代码给鲁豫看的话,她会说:我不信不信就不信啰,直接证明给你看,把代码中的两句注释语句恢复:

  1. #include <QCoreApplication>
  2. #include <iostream>
  3. using namespace std;
  4. int main(int argc, char *argv[])
  5. {
  6. QCoreApplication a(argc, argv);
  7. int* serven_p = 0;
  8. *serven_p = 4;
  9. printf("%p\n", serven_p);
  10. printf("%d\n", *serven_p);
  11. return a.exec();
  12. }
复制代码

运行结果:

结果分析:

        看到了吧,打印出来后啥都没有。这下可以相信了吧。

        但是在日常编程中,我们还是习惯使用NULL来给指针做初始化,但是NULL并不是C++的关键字,它是一个宏定义。在Visual Studio平台中,当你在编写代码中转至NULL的定义的时候,会跳到afx.h显示下面的代码:

  1. ...
  2. #define NULL 0
  3. ...
复制代码

  其实NULL宏定义就是一个0,惊不惊讶!反正我是挺惊讶的。好吧,既然它是0,那么就当它是空吧,这个披着外皮的狼,居然骗了我们这么多年。那就用它吧。

        等会,小编突然想到一个Bug,当NULL作为函数参数的时候会有歧义,例如:​​​​​​​

  1. #include <QCoreApplication>
  2. #include <iostream>
  3. using namespace std;
  4. void Func_serven(void* serven_void){
  5. cout<<"I am void fucntion!"<<endl;
  6. }
  7. void Func_serven(int serven_zero){
  8. cout<<"I am zero fucntion!"<<endl;
  9. }
  10. int main(int argc, char *argv[])
  11. {
  12. QCoreApplication a(argc, argv);
  13. Func_serven(NULL);
  14. Func_serven(0);
  15. return a.exec();
  16. }
复制代码

运行结果:

结果分析:

        对于参数0来讲,它确实是个0,那么调用函数Func_serven(int serven_zero)名正言顺。而对于参数NULL来讲,我们是要它传给第一个函数的才对,但是跟我们想的完全不一样。

        那么怎么样做才能达到我们的预期效果呢?很简单,在NULL参数的前面加上强制类型转换就可以了,将其转换成void*:​​​​​​​

  1. #include <QCoreApplication>
  2. #include <iostream>
  3. using namespace std;
  4. void Func_serven(void* serven_void){
  5. cout<<"I am void fucntion!"<<endl;
  6. }
  7. void Func_serven(int serven_zero){
  8. cout<<"I am zero fucntion!"<<endl;
  9. }
  10. int main(int argc, char *argv[])
  11. {
  12. QCoreApplication a(argc, argv);
  13.    Func_serven((void*) NULL);        // 强制类型转换
  14. Func_serven(0);
  15. return a.exec();
  16. }
复制代码

运行结果:

这样就OK啦!

        所以通过上面的例子,还是觉得使用NULL不太妥当。所以呢C++11标准委员会也考虑到这一点,但是他们又不想抛弃NULL这个宏定义,因为程序员已经对NULL很亲切了,突然之间抛弃它会很舍不得。所以呢在C++11新特性中引入了一个新的关键字nullptr。

        nullptr是nullptr_t类型的右值常量,专门用于初始化空类型的指针。而nullptr_t是在C++11新增加的数据类型,跟int类型的功能一样,只是int是整型类型,而nullptr_t是指针空值类型。也就是说呢,nullptr仅仅是该类型的一个实例对象(也就是已经定义好的,可以直接使用)。当然了,我们也可以定义出多个和nullptr完全一样的实例对象,例如:

  1. nullptr_t ser_ptr;
  2. int* serven_1 = ser_str;      // 定义一个整型指针,指向空
复制代码

        nullptr可以被隐式转换成任意的指针类型:

  1. int* serven_3 = nullptr;
  2. char* serven_4 = nullptr;
  3. double* serven_5 = nullptr;
复制代码

        不同类型的指针变量都可以使用nullptr类初始化,编译器会将nullptr隐式转换成int*、char*、double*指针类型。

        此外,nullptr也可以解决上面NULL的数参数调用的问题:

  1. #include <QCoreApplication>
  2. #include <iostream>
  3. using namespace std;
  4. void Func_serven(void* serven_void){
  5. cout<<"I am void fucntion!"<<endl;
  6. }
  7. void Func_serven(int serven_zero){
  8. cout<<"I am zero fucntion!"<<endl;
  9. }
  10. int main(int argc, char *argv[])
  11. {
  12. QCoreApplication a(argc, argv);
  13. Func_serven(nullptr);
  14.    nullptr_t ser_ptr;               // 你可以定义属于自己的空对象
  15. Func_serven(ser_ptr);
  16. Func_serven(0);
  17. return a.exec();
  18. }
复制代码

运行结果:

结果分析:

        因为nullptr无法隐式转换为整型,而可以隐式匹配指针类型,因此运行结果和我们预期的一样。另外,可以定义自己的空对象。

        但是使用nullptr_t一定要在支持C++11的编译平台上才可以使用。像Visual Studio平台都要2010版本之后才能使用。G++编译器至少要4.6.1才能使用。

总结:一般使用nullptr来编程会使我们的程序更加健壮。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Honkers

特级红客

关注
  • 3259
    主题
  • 36
    粉丝
  • 0
    关注
这家伙很懒,什么都没留下!

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行