Qt:NULL与nullptr的区别(手写nullptr)
前言
发现还是有人不知道NULL
与nullptr
的区别,故写此文章。
正文
对于NULL
先看NULL
的源码
我们可以看出这段代码是一个典型的预处理器宏定义块,用于处理 NULL
宏的定义。
先看开头
#if defined (_STDDEF_H) || defined (__need_NULL)
- 这行代码检查是否已经定义了
_STDDEF_H
或__need_NULL
。_STDDEF_H
通常是在包含标准库头文件<stddef.h>
时定义的宏。__need_NULL
是某些情况下(例如,部分标准头文件仅需要NULL
定义)会定义的宏。
如果其中一个宏已定义,则代码块会执行。
#undef NULL /* in case <stdio.h> has defined it. */
#undef NULL
取消之前可能在其他头文件(例如<stdio.h>
)中定义过的NULL
,确保后续的定义不会发生冲突。
#if defined(__GNUG__) && __GNUG__ >= 3
#define NULL __null
- 这一部分是针对 GNU C++ 编译器(G++)版本 3 及以上的特定定义。
__GNUG__
是 G++ 编译器的特定宏,用于检测当前编译器是否为 G++。__null
是 G++ 3.x 及以上版本中的内部表示,用于专门处理空指针的情况。
如果符合条件(G++ 3.x 及以上版本),NULL
被定义为 __null
。
#else /* G++ */
#ifndef __cplusplus
#define NULL ((void *)0)
- 如果当前编译环境不是 G++ 3.x 或更新版本,且不是 C++,那么
NULL
被定义为((void *)0)
。- 在 C 语言中,
NULL
通常定义为(void *)0
,表示一个空指针。
- 在 C 语言中,
#else /* C++ */
#ifndef _WIN64
#define NULL 0
- 进入了 C++ 的条件分支。
- 如果正在编译 C++ 代码,且不是 64 位 Windows 环境(未定义
_WIN64
),则NULL
定义为0
。 - 在 C++ 中,
NULL
通常直接定义为0
,因为 C++ 允许整数0
被隐式转换为指针类型。
- 如果正在编译 C++ 代码,且不是 64 位 Windows 环境(未定义
#else
#define NULL 0LL
#endif /* W64 */
#endif /* C++ */
- 如果是在 64 位 Windows 环境中,
NULL
定义为0LL
,表示一个 64 位的长整数常量(long long
)。- 在 64 位环境中,使用
0LL
以保证NULL
的大小与指针的大小一致。
- 在 64 位环境中,使用
#endif /* G++ */
#endif /* NULL not defined and <stddef.h> or need NULL. */
- 关闭之前的条件编译语句。
#endif
对应前面的#if
,表示条件编译的结束。
#undef __need_NULL
- 这行代码取消定义
__need_NULL
,确保之后不会再次使用这个宏。
总结
- 在 GNU C++ 3.x 及以上版本中,
NULL
定义为__null
。 - 在 C 语言中,
NULL
定义为(void *)0
,表示空指针。 - 在 C++ 中,
NULL
定义为0
,因为0
可以隐式转换为指针类型。 - 在 64 位 Windows 环境下,为了确保指针和
NULL
的大小一致,定义为0LL
。 - 由以上我们可知
NULL
既可以表示0又可以表示(void*)0
,这就说明它有二义性。
测试
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
int* ptr = NULL;
int b = 0;
int* ptr2 = nullptr;
QString* ptr3 = nullptr;
if (ptr == 0) {
qDebug()<<"ptr is null"<<ptr;
}
if (b == NULL) {
qDebug()<<"b is null"<<b;
}
if (ptr2 == 0) {
qDebug()<<"ptr2 is nullptr"<<ptr2;
}
if (!ptr3) {
qDebug()<<"ptr3 is nullptr"<<ptr3;
}
return a.exec();
}
运行结果
对于nullptr
我并没有找到它的源码(如果有找到的兄弟麻烦评论区告知下呗),但是我知道nullptr
是一种新的数据类型,就是专门用来表示空的一种数据类型。在源码中我只看到它简略的说明
nullptr
的出现就是为了解决NULL
即表示0又表示(void*)0
的二义性问题。
手写nullptr
const class nullptr_t
{
public:
// 模板类型转换运算符,可以将 nullptr 转换为任意类型的指针
template<class T>
inline operator T*() const
{
return 0; // 返回值为 0,表示空指针
}
// 模板类型转换运算符,可以将 nullptr 转换为任意类成员指针
template<class C, class T>
inline operator T C::*() const
{
return 0; // 返回值为 0,表示空成员指针
}
private:
// 重载地址取运算符(&),防止获取 nullptr 的地址
void operator&() const;
} nullptrSelf = {};
使用:
// widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
const class nullptr_t
{
public:
// 模板类型转换运算符,可以将 nullptr 转换为任意类型的指针
template<class T>
inline operator T*() const
{
return 0; // 返回值为 0,表示空指针
}
// 模板类型转换运算符,可以将 nullptr 转换为任意类成员指针
template<class C, class T>
inline operator T C::*() const
{
return 0; // 返回值为 0,表示空成员指针
}
private:
// 重载地址取运算符(&),防止获取 nullptr 的地址
void operator&() const;
} nullptrSelf = {};
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void test(int* a);
private:
void* memberPtr;
};
#endif // WIDGET_H
// widget.cpp
#include "widget.h"
#include "qdebug.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
memberPtr = nullptrSelf;
qDebug()<<"memberPtr:"<<memberPtr;
int* a = nullptrSelf;
test(a);
}
Widget::~Widget()
{
}
void Widget::test(int* a)
{
if (a == nullptrSelf) {
qDebug()<<"a is null "<<a;
} else {
qDebug()<<"a is not null "<<a;
}
}
输出结果
小结
请指正