【发布时间】:2012-02-18 16:17:55
【问题描述】:
我在许多最顶层的头文件中看到了下面的宏:
#define NULL 0 // C++03
在所有代码中,NULL 和 0 可以互换使用。如果我把它改成。
#define NULL nullptr // C++11
会不会造成不良的副作用?我能想到的唯一(好的)副作用是以下用法会变得不正确;
int i = NULL;
【问题讨论】:
我在许多最顶层的头文件中看到了下面的宏:
#define NULL 0 // C++03
在所有代码中,NULL 和 0 可以互换使用。如果我把它改成。
#define NULL nullptr // C++11
会不会造成不良的副作用?我能想到的唯一(好的)副作用是以下用法会变得不正确;
int i = NULL;
【问题讨论】:
我在最顶层的头文件中看到了下面的宏:
你不应该看到,标准库在<cstddef>(和<stddef.h>)中定义了它。而且,IIRC,根据标准,重新定义标准头文件定义的名称会导致未定义的行为。所以从纯粹的标准角度来看,你不应该这样做。
我见过人们做以下事情,无论出于何种原因,他们的心碎了:
struct X{
virtual void f() = NULL;
}
(如[错误]:“将虚拟表指针设置为NULL”)
这仅在 NULL 定义为 0 时有效,因为 = 0 是纯虚函数 (§9.2 [class.mem]) 的有效标记。
也就是说,如果NULL正确用作空指针常量,那么什么都不会中断。
但是,请注意,即使看似正确使用,这也会改变:
void f(int){}
void f(char*){}
f(0); // calls f(int)
f(nullptr); // calls f(char*)
但是,如果是这样的话,无论如何它几乎肯定是坏了。
【讨论】:
virtual void f() = 0L; 合法吗?因为#define NULL 0L 肯定是符合规范的实现。
NULL 已经被标准定义了。
NULL 扩展为实现所说的任何扩展的代码。在 NULL 的情况下,如果实现(不明智地 IMO,但合法)在函数需要指针的上下文中将 NULL 作为可变参数传递,则可能会有所不同,因为知道 在其调用下是安全的约定,NULL 大小合适,可以使用。如果您在包含该标头之前将其从 0L 更改为 0,那么您可能会破坏它。
§9.2 [class.mem]: pure-specifier: = 0.
#define NULL nullptr 对你来说是合法的,因为 nullptr 是一个空指针常量,而 NULL 可以是任何空指针常量。但是程序不能自己决定应该是哪一个,这取决于实现。
更好的是在整个代码中搜索 NULL 并将其替换为 nullptr。
它在语法上可能是安全的,但你会将#define 放在哪里?它会产生代码组织问题。
【讨论】:
g++ -DNULL=nullptr ... :)
-Dmode 开关是合理的,假设没有它代码仍然可以编译。此外,并非每个项目都需要跨平台,但在这种情况下,您不需要 -DWindows 或 -DAnythingElse,因为它是内置的。
没有。您不能(重新)定义标准宏。如果你看到
#define NULL 0
在标准头以外的任何文件的顶部(即使在那里,它 应该在包含警卫中,通常在附加警卫中作为 好),那么那个文件就坏了。删除它。
请注意,好的编译器通常会用一些东西定义NULL
喜欢:
#define NULL __builtin_null
,访问一个编译器,如果它是会触发一个警告 在非指针上下文中使用。
【讨论】:
您根本不应该定义它,除非您正在编写自己的<cstddef> 版本;它当然不应该出现在“许多最顶层的头文件”中。
如果您要实现自己的标准库,那么唯一的要求是
18.2/3 宏NULL是实现定义的C++空指针常量
所以0 或nullptr 都是可以接受的,而nullptr 更好(如果你的编译器支持的话)因为你给出的原因。
【讨论】:
也许不会
如果您有特定格式的重载行为:
void foo(int);
void foo(char*);
然后是代码的行为:
foo(NULL);
将根据NULL是否更改为nullptr而改变。
当然,还有另一个问题是编写此答案中存在的代码是否安全...
【讨论】:
虽然它可能会破坏与写得不好的旧东西的向后兼容性(要么那样,要么过于聪明......),但对于您的新代码,这不是问题。你应该使用nullptr,而不是NULL,你的意思是nullptr。另外,你应该使用0,你的意思是零。
【讨论】:
nullptr,因为它只是几个月前才添加到语言中。
#define NULLPTR NULL的东西,但是当编译为C++ 11时可以更改为#define NULLPTR nullptr。然后,一旦您确信您将不再需要将其编译为 C++03,则代码中的所有 NULLPTR 实例都会被删除。
nullptr,而不是NULLPTR。就我个人而言,我不会在 C++03 中为我知道将成为关键字的符号定义宏,这就是为什么我说 NULLPTR 而不是 nullptr。但它完全有效,前提是您确保在构建为 C++11 时未定义宏。