C++中见到 `::` 开头的代码就吓退?

159 阅读3分钟

一、操作符 :: 的使用情况

在 C++ 中,操作符 :: 可以用于以下几种情况:

  1. 作用域解析运算符 操作符 :: 可以用于访问命名空间、类、结构体、联合体、枚举等作用域中的成员。例如,对于类中的静态成员变量或静态成员函数,可以使用作用域解析运算符来访问,如 MyClass::myStaticVar。
  2. 全局作用域限定符 当需要使用全局作用域中的标识符时,可以使用 :: 操作符来限定,例如 ::myGlobalVar。
  3. 成员指针运算符 可以使用 ::* 操作符来定义指向成员函数的指针,例如 void (MyClass::*ptr)() = &MyClass::myMemberFunc。
  4. 基类访问限定符 在派生类中,可以使用 :: 操作符来访问基类中的成员,例如 BaseClass::myFunc()。
  5. 标记分隔符 在 C++11 及之后的版本中,:: 可以用作标记分隔符,例如 std::chrono::duration。

需要注意的是,在使用作用域解析运算符时,左侧必须是作用域,右侧是作用域中的成员。而在使用全局作用域限定符时,左侧必须是 ::,右侧是全局作用域中的标识符。

二、全局作用域限定符::

重点说一下全局作用域限定符::

2.1 实例

命名空间 NAMESPACE_A 中的 CLASS_A 类中有一个函数 InitApp,如下所示,此处省略更多代码细节。

namespace NAMESPACE_A
{
    class CLASS_A
    {
    public:
            CLASS_A();
            ~CLASS_A();

            bool InitApp();
            ...
    }
}

它的具体实现是这样的,这里的函数调用为什么前面有 :: 呢?

bool CLASS_A::InitApp()
{
        if (m_bInitialized)	return true;

        try
        {
                ::odrxInitialize(&g_svcs);
                // Load ODA BimRv modules
                ::odrxDynamicLinker()->loadModule(OdBmBaseModuleName);
                ::odrxDynamicLinker()->loadModule(OdBmLoaderModuleName, false);
                ::odrxDynamicLinker()->loadModule(OdBmDevGuideCommands);
                ::odrxDynamicLinker()->loadModule(OdBmCommands);
                ::odrxDynamicLinker()->loadModule(OdBmModelerModuleName, false);
                ::odrxDynamicLinker()->loadModule(OdBmModelerCommands, false);
                m_bInitialized = true;
        }
        catch (...)
        {
                ::odrxUninitialize();
        }
}

在这段代码中,"::"是C++中的作用域操作符,用于指定命名空间或全局作用域。例如,"::odrxInitialize" 表示调用全局作用域下的odrxInitialize函数。

为什么必须加上 :: 而不能直接或者是用其他方式调用呢?以 odrxInitialize 为例,它的原型是

FIRSTDLL_EXPORT bool odrxInitialize(OdRxSystemServices* pSysSvcs);,来自Oda的第三方库 Kernel,所以需要告诉编译器在全局作用域中查找函数。如果不使用::限定符,编译器会尝试在当前命名空间或嵌套命名空间中查找odrxInitialize函数,而不是在全局命名空间中查找。如果当前命名空间中有同名的函数,编译器会优先选择它而不是::odrxInitialize。因此,为了确保调用的是全局命名空间中的odrxInitialize函数,需要使用::限定符。

三、何时使用全局作用域::调用?

调用全局作用域下的函数有多种原因,以下列举几个常见的情况:

  1. 函数是由其他库模块提供的,需要在当前模块中调用该函数时,需要使用全局作用域下的函数名。
  2. 函数名在当前作用域中被覆盖了,需要调用全局作用域中的原始函数。
  3. 函数定义在全局作用域中,而当前作用域中无法访问该函数,需要使用全局作用域下的函数名。
  4. 在某些情况下,函数可能被定义为内部函数,即仅在当前模块内部使用。在这种情况下,如果需要从当前模块外部访问该函数,则需要使用全局作用域下的函数名。