关于C++:前置双冒号::的含义是什么?

What is the meaning of prepended double colon “::”?

我在一个必须修改的类中发现了这行代码:

1
::Configuration * tmpCo = m_configurationDB;//pointer to current db

我不知道在类名前面加上双冒号的确切含义是什么。如果没有这个,我会读到:tmpCo声明作为指向类Configuration对象的指针…但是预先准备好的双结肠让我困惑。

我还发现:

1
typedef ::config::set ConfigSet;


这样可以确保从全局命名空间进行解析,而不是从当前所在的命名空间开始。例如,如果您有两个不同的类,称为Configuration,那么:

1
2
3
4
5
6
7
8
9
10
11
12
class Configuration; // class 1, in global namespace
namespace MyApp
{
    class Configuration; // class 2, different from class 1
    function blah()
    {
        // resolves to MyApp::Configuration, class 2
        Configuration::doStuff(...)
        // resolves to top-level Configuration, class 1
        ::Configuration::doStuff(...)
    }
}

基本上,它允许您遍历到全局名称空间,因为您的名称可能会被另一个名称空间内的新定义(在本例中是MyApp)所破坏。


::运算符被称为作用域解析运算符,仅此而已,它解析作用域。因此,通过在类型名前面加上这个前缀,它告诉编译器在全局命名空间中查找该类型。

例子:

1
2
3
4
5
6
7
8
int count = 0;

int main(void) {
  int count = 0;
  ::count = 1;  // set global count to 1
  count = 2;    // set local count to 2
  return 0;
}


已经有很多合理的答案了。我将加入一个可以帮助一些读者的类比。当您在路径中搜索要运行的程序时,::的工作方式与文件系统目录分隔符'/非常相似。考虑:

1
/path/to/executable

这是非常明确的——无论路径是什么,只有文件系统树中该位置的可执行文件才能匹配此规范。类似地…

1
::std::cout

……在C++命名空间"树"中同样显式。

与这些绝对路径相比,您可以配置好的unix shell(例如zsh)来解析当前目录下的相对路径或PATH环境变量中的任何元素,因此,如果PATH=/usr/bin:/usr/local/bin和您"在"/tmp中,那么……

1
X11/xterm

…如果找到,会很高兴地运行/tmp/X11/xterm,否则/usr/bin/X11/xterm,否则/usr/local/bin/X11/xterm。类似地,假设您在一个名为X的名称空间中,并且有一个"using namespace Y"有效,那么……

1
std::cout

…可以在任何一个::X::std::cout::std::cout::Y::std::cout中找到,也可以在其他地方找到,这可能是由于依赖于参数的查找(adl,aka koenig lookup)。因此,只有::std::cout才真正明确你所指的对象,但幸运的是,他们头脑中没有人会创建自己的类/结构或名称空间,称为"std",也没有人会创建任何称为"cout"的东西,因此在实践中只使用std::cout是可以的。

值得注意的差异:

1)壳牌倾向于使用EDCOX1(2)中的排序来使用第一匹配,而C++在您不明确时给出编译器错误。

2)在C++中,没有任何超前范围的名称可以在当前命名空间中匹配,而大多数UNIX shell只在EDOCX1 2中插入EDOCX1×18。

3)C++总是搜索全局命名空间(就像EDCOX1,1,隐式的EDCOX1,2)一样。

符号的名称空间和显式性的一般讨论

使用absolute ::abc::def::...的"路径"有时有助于将您与正在使用的任何其他名称空间隔离开来,这是您的库的一部分,但实际上并不控制库的内容,甚至是您的库的客户端代码也使用的其他库的内容。另一方面,它还将您与符号的现有"绝对"位置结合得更紧密,并且您会错过名称空间中隐式匹配的优势:耦合更少、名称空间之间的代码更容易移动,以及更简洁、可读的源代码。

和许多事情一样,这是一种平衡行为。C++标准在EDOCX1的23个标识符下放置了比EDCOX1(15)更不"唯一"的标识符,程序员可能会使用它们代码中完全不同的东西(例如EDCOX1,25,EDCOX1,26,EDCOX1,27,EDCX1,28,EDOCX1,29,EDCX1,30,EDCX1,31,EDCOX1 32)。两个不相关的非标准库使用相同的标识符的可能性要高得多,因为作者通常不了解或不太了解彼此。库(包括C++标准库)随时间改变它们的符号。所有这些都可能在重新编译旧代码时产生歧义,特别是在大量使用using namespaces的情况下:在这个空间中,最糟糕的做法是允许using namespaces在头中退出头的作用域,这样,任意数量的直接和间接客户机代码就无法做出自己的决定abo使用哪些名称空间以及如何管理模糊性。

因此,一个领先的EDCOX1(0)是C++程序员工具箱中的一个工具,用来主动消除已知的冲突,和/或消除未来歧义的可能性。


::是作用域解析操作符。它用于指定某个对象的范围。

例如,只有::是全局范围,不包括所有其他名称空间。

some::thing可以用以下任何一种方式解释:

  • some是一个名称空间(在全局范围内,或是一个比当前范围更大的外部范围内),thing是一个类型、函数、对象或嵌套的名称空间;
  • some是当前范围内可用的类,thingsome类的成员对象、函数或类型;
  • 在类成员函数中,some可以是当前类型(或当前类型本身)的基类型,而thing则是该类、类型、函数或对象的一个成员。

也可以有嵌套的作用域,如some::thing::bad中所示。在这里,每个名称可以是类型、对象或名称空间。另外,最后一个函数bad也可以是函数。其他函数不能,因为函数不能在其内部范围内公开任何内容。

因此,回到您的示例,::thing只能是全局范围内的东西:类型、函数、对象或名称空间。

使用它的方式表明(在指针声明中使用)它是全局范围中的类型。

我希望这个答案是完整和正确的,足以帮助您理解范围解析。


::用于将某物(变量、函数、类、typedef等)链接到命名空间或类。

如果在::之前没有左边,那么它强调了您正在使用全局命名空间的事实。

例如。:

::doMyGlobalFunction();


它被称为作用域解析操作符,使用作用域解析操作符可以引用隐藏的全局名称:例如;

1
2
3
4
5
6
7
8
int x;
void f2()
{
   int x = 1; // hide global x
   ::x = 2; // assign to global x
   x = 2; // assign to local x
   // ...
}


(这个答案主要是针对谷歌,因为OP已经解决了他的问题。)预先准备好的::范围恢复操作符的含义在其他答案中也有描述,但我想补充一下人们为什么要使用它。

意思是"从全局名称空间中取名字,而不是其他任何东西"。但为什么这需要明确拼写?

用例-命名空间冲突

在全局命名空间和本地/嵌套命名空间中使用相同的名称时,将使用本地名称。所以,如果你想要全球性的,就用::来提前。这个案例是在@wyatt anderson的回答中描述的,请看他的例子。

用例-强调非成员函数

在编写成员函数(方法)时,对其他成员函数的调用和对非成员(自由)函数的调用看起来类似:

1
2
3
4
5
6
7
8
9
10
11
12
13
class A {
   void DoSomething() {
      m_counter=0;
      ...
      Twist(data);
      ...
      Bend(data);
      ...
      if(m_counter>0) exit(0);
   }
   int m_couner;
   ...
}

但也有可能是TwistA类的姐妹成员函数,Bend是自由函数。也就是说,Twist可以使用和修改m_counerBend不能。因此,如果你想确保m_counter保持0,你必须检查Twist,但不需要检查Bend

为了使这一点更加突出,我们可以编写this->Twist来向读者展示Twist是一个成员函数,或者编写::Bend来展示Bend是免费的。或者两者兼而有之。这在您进行或计划重构时非常有用。


::是定义命名空间的运算符。

例如,如果要使用cout而不在代码中提及using namespace std;,请编写以下代码:

1
std::cout <<"test";

当没有提到名称空间时,就说类属于全局名称空间。


"::"表示范围解析运算符。具有相同名称的函数/方法可以在两个不同的类中定义。要访问特定类范围的方法,使用解析运算符。