同事经常写这样的话:
::someObject->someMethod(anAttribute, anotherAttribute);
someObject
是一个全局变量
那两个冒号对我来说很奇怪。代码编译并在没有它们的情况下运行良好。
同事声称这些冒号使someObject
明确地全球化,从而防止与可能的本地someObject
混淆。我认为如果已经全局定义了someObject
,那么你将无法在本地定义它。
你能否了解那些冒号的含义以及它们是否必要?
答案 0 :(得分:8)
你的同事是对的。您确实可以定义一个本地someObject
,它会隐藏该范围内的全局someObject
:
SomeClass* someObject = ...;
// here the global object is visible
someObject->someMethod(anAttribute, anotherAttribute); // calls the global object
void someMethod() {
SomeClass* someObject = ...;
// here the local object hides the global
::someObject->someMethod(anAttribute, anotherAttribute); // calls the global object
someObject->someMethod(anAttribute, anotherAttribute); // calls the local object
}
// here again only the global object is visible
someObject->someMethod(anAttribute, anotherAttribute); // calls the global object
范围可以递归地嵌入到其他范围内,因此您可以在全局范围内具有命名空间,命名空间中的类,类中的内部类,内部类中的方法,方法中的块...您可以在任何这些范围中使用相同名称的变量/类/方法/ ....因此,识别特定名称所指的实体并不是C ++中的一项微不足道的任务。它被称为name lookup。
简而言之,只要编译器找到一个名称,它就会从最里面的范围开始查找该名称。即在someMethod
内,名称someObject
与本地定义的对象匹配。 ::someObject
会覆盖此默认行为,并使编译器仅在最外层(全局)范围内进行搜索,从而找到全局对象而不是本地对象。
答案 1 :(得分:4)
你确实可以在本地定义一个someObject,即使它有一个全局的。这两个变量具有不同的范围,因此编译器知道两者之间存在差异,而双冒号则允许您引用全局变量。
这也适用于命名空间之外的类;即,要从类Bar :: Foo中引用类Foo,您可以使用:: Foo告诉编译器您正在讨论的是不在命名空间中的那个。
以下示例显示了它的工作原理:
#include<stdio.h>
int someInt = 4;
int main() {
int someInt = 5;
printf("%d", someInt+::someInt);
return 0;
}
如果编译并运行该段代码,它将输出9。
答案 2 :(得分:3)
同事声称那些冒号 使someObject显式为全局和 从而防止与可能的混淆 本地someObject。
是 - 这意味着该功能可以并且必须仅在全局命名空间中匹配。它显然是你正在处理全局,并且会阻止意外匹配更本地的东西(beit函数本地,对象成员,命名空间成员,你正在使用的命名空间,Koenig查找匹配等)。 / p>
我认为你不会 如果能够在本地定义someObject 它已经在全球定义了吗?
将它变成错误是一个非常糟糕的主意。假设一个程序员团队决定他们想要在他们的命名空间中添加一个名为“last_error”的变量:他们不应该担心命名空间中的现有函数是否对局部变量使用相同的名称。如果将函数从一个名称空间或类复制到另一个名称空间或类,则不必在实现中进行容易出错的标识符替换。
关于::的好处,请考虑:
namespace X
{
void fn() {
rip(data, bytes); // MP3 rip this data
}
}
...然后需要将fn()
快速移动到命名空间Y ...
namespace Y
{
void rip(const char* message, int exit_code); /* for fatal errors */
...
}
...随意复制粘贴到Y的内容中可能很容易忽略日志与fn在命名空间X中时所使用的相同全局函数不匹配,但是 - 如图所示 - 功能可能显着不同: - 。)
你可以想到构成一个树的每个命名空间,类/结构和函数,其中每个级别必须是唯一键控的(即在同一名称空间中没有同名的类),但是后代总是彼此独立并且它们的祖先是独立的。 。增加独立变化的自由对于让许多人同时处理大问题至关重要。
你能否解释一下 那些冒号意味着它们是否是 必要?
在这个特定的用法中,::可能不是绝对必要的。它增加了一些保护,但是后来更难将变量移动到更局部的范围 - 虽然这不是那么重要,因为编译器会告诉你在移动x之后继续引用:: x的位置。 p>
答案 3 :(得分:1)
只是对响应中出现的所有其他好点的一点点补充。
基本上::在全局命名空间中调用限定名称查找。但是,不能保证无问题,特别是在滥用指令的情况下。
显示的代码说明了限定名称查找的重要属性。这里的要点是搜索在全局namspace中使用指令指定的名称空间。但是,会跳过在名称被搜索的名称空间中使用指令。
namespace Z{
void f(){cout << 1;}
}
namespace Y{
void f(){cout << 2;}
using namespace Y; // This namespace is not searched since 'f' is found in 'Y'
}
#if 0
namespace Z{
void f(){cout << 3;}
using namespace Y; // This namespace is not searched since 'f' is found in 'Y'
}
using namespace Z;
#endif
using namespace Y;
int main(){
::f(); // Prints 2. There is no ambiguity
}
在显示的代码中,如果启用了带#if指令的行,则解析名称'f'时会有歧义。