一、操作符 :: 的使用情况
在 C++ 中,操作符 :: 可以用于以下几种情况:
- 作用域解析运算符 操作符 :: 可以用于访问命名空间、类、结构体、联合体、枚举等作用域中的成员。例如,对于类中的静态成员变量或静态成员函数,可以使用作用域解析运算符来访问,如 MyClass::myStaticVar。
- 全局作用域限定符 当需要使用全局作用域中的标识符时,可以使用 :: 操作符来限定,例如 ::myGlobalVar。
- 成员指针运算符 可以使用 ::* 操作符来定义指向成员函数的指针,例如 void (MyClass::*ptr)() = &MyClass::myMemberFunc。
- 基类访问限定符 在派生类中,可以使用 :: 操作符来访问基类中的成员,例如 BaseClass::myFunc()。
- 标记分隔符 在 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函数,需要使用::限定符。
三、何时使用全局作用域::调用?
调用全局作用域下的函数有多种原因,以下列举几个常见的情况:
- 函数是由其他库或模块提供的,需要在当前模块中调用该函数时,需要使用全局作用域下的函数名。
- 函数名在当前作用域中被覆盖了,需要调用全局作用域中的原始函数。
- 函数定义在全局作用域中,而当前作用域中无法访问该函数,需要使用全局作用域下的函数名。
- 在某些情况下,函数可能被定义为内部函数,即仅在当前模块内部使用。在这种情况下,如果需要从当前模块外部访问该函数,则需要使用全局作用域下的函数名。