在C++中,用某种前缀命名成员变量是很常见的,以表示它们是成员变量,而不是局部变量或参数。如果你来自MFC背景,你可能会使用m_foo。我偶尔也会看到myFoo。
m_foo
myFoo
C#(或者可能只是. NET)似乎建议只使用下划线,如_foo。C++标准允许这样做吗?
_foo
是的,下划线可以在标识符的任何地方使用。我相信规则是:a-z、A-Z、_中的任何一个在第一个字符中,然后是+0-9的字符。
下划线前缀在C代码中很常见——单个下划线表示“私有”,双下划线通常保留给编译器使用。
从MSDN:
( __ ) 标识符开头使用两个连续的下划线字符,或在单个前导下划线后跟大写字母,是为所有范围内的C++实现保留的。对于具有文件范围的名称,应避免使用一个前导下划线后跟小写字母,因为可能与当前或将来的保留标识符发生冲突。
这意味着您可以使用单个下划线作为成员变量前缀,只要它后面跟着一个小写字母。
这显然取自C++标准的第17.4.3.1.2节,但我在网上找不到完整标准的原始来源。
另见这个问题。
规则(在C++11中没有改变):
std
2003年C++标准:
17.4.3.12全球名称[lib.global.names] 某些名称和函数签名集始终保留给实现: 每个包含双下划线(__)或以下划线后跟大写字母(2.11)开头的名称都保留给实现以供任何使用。每个以下划线开头的名称都保留给实现用作全局命名空间中的名称。165 165)这样的名称也保留在命名空间::std(17.4.3.1)中。
某些名称和函数签名集始终保留给实现:
__
165)这样的名称也保留在命名空间::std(17.4.3.1)中。
::std
因为C++是基于C标准(1.1/2,C++03)和C99是一个规范性参考(1.2/1,C++03),这些也适用于1999 C标准:
7.1.3保留标识符 每个标头声明或定义其关联子句中列出的所有标识符,并且可选地声明或定义在其关联的未来库方向子句中列出的标识符和标识符,这些标识符始终保留用于任何用途或用作文件范围标识符。 以下划线和大写字母或其他字母开头的所有标识符下划线始终保留用于任何用途。所有以下划线开头的标识符始终保留用作标识符在普通和标记名称空间中使用文件范围。以下任何子句中的每个宏名称(包括未来的库)如果包含任何与其关联的标题,则保留用于指定的用途;除非另有明确说明(见7.1.4)。以下任何子句中具有外部链接的所有标识符(包括未来库方向)始终保留用作外部标识符链接。154每个标识符的文件范围列在以下任何子句中(包括未来库方向)保留用作宏名称和标识符如果包含任何与其关联的标头,则在同一名称空间中的文件范围。 不保留其他标识符。如果程序在它被保留的上下文(7.1.4允许的情况除外),或定义保留的上下文标识符作为宏名称,行为未定义。 如果程序删除(使用#undef)第一个标识符的任何宏定义上面列出的组,行为未定义。 154)具有外部链接的保留标识符列表包括errno、math_errhandling、setjmp和va_end。
每个标头声明或定义其关联子句中列出的所有标识符,并且可选地声明或定义在其关联的未来库方向子句中列出的标识符和标识符,这些标识符始终保留用于任何用途或用作文件范围标识符。
不保留其他标识符。如果程序在它被保留的上下文(7.1.4允许的情况除外),或定义保留的上下文标识符作为宏名称,行为未定义。
如果程序删除(使用#undef)第一个标识符的任何宏定义上面列出的组,行为未定义。
#undef
154)具有外部链接的保留标识符列表包括errno、math_errhandling、setjmp和va_end。
errno
math_errhandling
setjmp
va_end
其他限制可能适用。例如,POSIX标准保留了许多可能出现在正常代码中的标识符:
E
is
to
LC_
f
l
SIG
SIG_
str
mem
wcs
PRI
SCN
X
_t
虽然现在为自己的目的使用这些名称可能不会造成问题,但它们确实会增加与该标准的未来版本发生冲突的可能性。
就我个人而言,我只是不以下划线开头标识符。我的规则的新补充:不要在任何地方使用双下划线,这很容易,因为我很少使用下划线。
在研究了这篇文章之后,我不再以_t结束我的标识符因为这是由POSIX标准保留的。
关于任何以_t结尾的标识符的规则让我大吃一惊。我认为这是一个POSIX标准(还不确定),正在寻找澄清和官方章节和诗句。这来自GNU库工具手册,列出保留名称。
CesarB提供了POSIX 2004保留符号和注释的以下链接,“可以在那里找到许多其他保留前缀和后缀……”。该POSIX 2008保留符号在这里定义。限制比上面的更细微。
避免名称冲突的规则既在C++标准中(参见Stroustrup书),也在C++大师(Sutter等)中提到。
因为我不想处理案例,想要一个简单的规则,我设计了一个个人既简单又正确的规则:
命名符号时,如果您:
当然,将代码放在一个唯一的命名空间中也有助于避免冲突(但不会防止邪恶的宏)
(我使用宏是因为它们对C/C++符号的代码污染更大,但它可以是从变量名到类名的任何东西)
#define _WRONG#define __WRONG_AGAIN#define RIGHT_#define WRONG__WRONG#define RIGHT_RIGHT#define RIGHT_x_RIGHT
从n3242.pdf文件(我希望最终的标准文本类似):
17.6.3.32全球名称[global.names] 某些名称和函数签名集始终保留给实现: -包含双下划线__或以下划线后跟大写字母(2.12)开头的每个名称都保留给实现以供任何使用。 -以下划线开头的每个名称都保留给实现用作全局命名空间中的名称。
17.6.3.32全球名称[global.names]
-包含双下划线__或以下划线后跟大写字母(2.12)开头的每个名称都保留给实现以供任何使用。
-以下划线开头的每个名称都保留给实现用作全局命名空间中的名称。
但也:
17.6.3.3.5用户定义的文字后缀[usrlit.suffix] 不以下划线开头的文字后缀标识符保留用于将来的标准化。
17.6.3.3.5用户定义的文字后缀[usrlit.suffix]
不以下划线开头的文字后缀标识符保留用于将来的标准化。
最后一个子句令人困惑,除非您认为如果在全局命名空间中定义没有,则以一个下划线开头并后跟小写字母的名称将是OK…
至于问题的另一部分,通常将下划线放在变量名的结束处,以避免与任何内部内容发生冲突。
我甚至在类和命名空间中也这样做,因为我只需要记住一个规则(与“在全局范围内的名称末尾,其他地方的名称开头”相比)。