代码之家  ›  专栏  ›  技术社区  ›  bastibe

如果一个方法调用以两个冒号开始,这意味着什么?

  •  8
  • bastibe  · 技术社区  · 14 年前

    同事经常这样写:

    ::someObject->someMethod(anAttribute, anotherAttribute);
    

    someObject 是全局变量。
    我觉得这两个冒号很奇怪。没有它们,代码就可以编译和运行。

    同事声称这些冒号 某物 明确的全局性,从而避免与可能的本地 某物 . 我认为你无法定义 某物 是否已在本地定义?

    你能解释一下这些冒号是什么意思吗?它们是否必要吗?

    4 回复  |  直到 14 年前
        1
  •  8
  •   Péter Török    14 年前

    你的同事是对的。您确实可以定义本地 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 重写此默认行为并使编译器只在最外层(全局)范围内搜索,因此它将查找全局对象而不是本地对象。

        2
  •  4
  •   Michael Madsen    14 年前

    即使存在全局对象,也可以在本地定义某个对象。这两个变量的作用域不同,所以编译器知道这两个变量之间有区别,而双冒号让您引用全局变量。

    这也适用于命名空间之外的类;例如,要从类栏::foo引用类foo,可以使用::foo来告诉编译器您所说的是命名空间之外的类。

    下面是一个例子,说明了它的工作原理:

    #include<stdio.h>
    
    int someInt = 4;
    
    int main() {
            int someInt = 5;
            printf("%d", someInt+::someInt);
            return 0;
    }
    

    如果您编译并运行这段代码,它将输出9。

        3
  •  3
  •   Tony Delroy    14 年前

    同事声称那些冒号 使某个对象显式全局化并 从而防止与可能的 本地SomeObject。

    是-这意味着函数可以并且只能在全局命名空间中匹配。很明显,您正在处理一个全局,并且可以防止意外地与更局部的对象(beit函数local、对象成员、命名空间成员、正在使用的命名空间、koenig查找匹配等)进行匹配。

    我想你不会 能够在本地定义某个对象,如果 它已经在全球范围内定义?

    犯错误是个很坏的主意。假设一个程序员团队决定向他们的名称空间中添加一个名为“最后一个错误”的变量:如果名称空间中的现有函数对局部变量使用相同的名称,他们不必担心。如果您将一个函数从一个名称空间或类复制到另一个名称空间或类,则不必在实现中进行容易出错的标识符替换。

    关于::的好处,请考虑:

    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中时使用的全局函数不匹配,但是-如图所示-功能可能显著不同:-)。

    您可以想到构成树的每个名称空间、类/结构和函数,其中每个级别都必须是唯一键控的(即在同一名称空间中没有相同的命名类),但decendent始终独立于彼此及其祖先。增加独立变化的自由对于让许多人同时处理一个大问题是至关重要的。

    你能解释一下什么吗 那些冒号的意思是,不管它们是不是 必要吗?

    在这个特定的用法中,可能不需要使用::。它增加了一点保护,但使以后将变量移动到更本地的作用域更加困难——尽管这并不重要,因为编译器将告诉您移动x后继续引用::x的位置。

        4
  •  1
  •   Chubsdad    14 年前

    只是一个小的附加到所有其他的好点,已经在回应。

    基本上::在全局命名空间中调用限定名查找。然而,它不能保证没有问题,特别是在面对不加区分的使用指令时。

    所示代码说明了限定名查找的一个重要属性。这里的要点是搜索使用全局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”时会出现歧义。