11

我正在查看一些(C++)代码,发现了这样的东西:

//Foo.cpp
namespace
{
    void SomeHelperFunctionA() {}
    void SomeHelperFunctionB() {}
    void SomeHelperFunctionC() {}

    //etc...    

    class SomeClass //<---
    {
        //Impl
    };
}

SomeHelperFunction[A-Z]是仅在该翻译单元中需要的功能,所以我理解它们为什么在匿名namespace. 同样,SomeClass也仅在该翻译单元中需要,但我的印象是,如果您没有全局类声明(例如,在一个通常包含的头文件)。

我还应该提到,这个特定的翻译单元不包含任何可能声明具有相同名称 ( SomeClass) 的类的标头。

那么,鉴于这些信息,有人可以解释一下为什么最初的程序员可能会这样做吗?也许只是作为未来的预防措施?

老实说,我以前从未见过匿名命名空间中使用的类。

谢谢!

4

5 回答 5

7

匿名命名空间在全局级别应用时就像静态关键字。

匿名命名空间使您无法从另一个文件调用命名空间内的任何内容。

匿名命名空间允许您将内容的范围限制在当前文件中。

程序员会这样做以避免命名冲突。在链接时没有全局名称会以这种方式发生冲突。

例子:

文件:test.cpp

namespace 
{
  void A()
  {
  }
  void B()
  {
  }
  void C()
  {
  }
}

void CallABC()
{ 
  A();
  B();
  C();
}

文件:main.cpp

void CallABC();//You can use ABC from this file but not A, B and C

void A()
{
//Do something different
}

int main(int argc, char** argv)
{
  CallABC();
  A();//<--- calls the local file's A() not the other file. 
  return 0;
}

以上将编译正常。但是,如果您尝试CallABC()在 main 中编写函数,则会出现链接错误。

通过这种方式,您不能单独调用A(),B()C()函数,但您可以调用CallABC()它们一个接一个地调用它们。

CallABC()您可以在 main.cpp中转发声明并调用它。但是您不能在 main.cpp 中转发声明 test.cpp 的 A()、B() 或 C(),因为您将遇到链接错误。

至于为什么命名空间里面有一个类。这是为了确保没有外部文件使用这个类。.cpp 中的某些内容可能使用了该类。

于 2009-07-18T16:14:22.350 回答
1

如果有人链接此代码并包含相同命名类的定义,并且此文件在其他实现之前链接,则您将发生名称冲突。

于 2009-07-18T16:18:32.763 回答
1

在 C++ ISO 标准(第 2.3 节)中,您会发现一个名为The One Definition Rule 的规则

由于 C++ 中编译和链接之间的复杂关系,这不是一个很简单的规则。大部分细节都在这里

但它适用于作为类成员的函数,因为它们(在链接级别)只是具有长名称的函数。

它以稍微不同的方式应用于模板,因为链接器会很高兴地丢弃出现在单独翻译单元(源文件)中的模板类或函数的额外定义。这意味着如果您为同一模板提供两个不同的定义,您的程序将具有未定义的行为(最佳情况:其中一个定义将被静默随机选择)。

于 2009-07-18T21:20:44.523 回答
1

我的印象是,只要您没有全局类声明,您就可以在不同的翻译单元中拥有具有相同名称的类而不会发生任何类型的命名冲突

好吧,事实并非如此。请记住,那些“通用”全局类定义位于头文件中。这些是字面上包含的,将通用的全局类定义复制到所有翻译单元。如果您使用另一种方式在多个翻译单元中包含完全相同的类定义(例如宏扩展),也可以。但是如果你对同一个类名有不同的定义,你就有未定义行为的风险。如果幸运的话,链接失败。

于 2009-07-20T10:58:13.663 回答
0

要直接回答您的问题,我认为这是为了避免 SomeClass 的静态成员出现链接器“多重定义”错误。也就是说,假设 SomeClass 是在没有匿名命名空间的 cpp 文件中定义的,并且其中有一些静态字段和/或静态方法,并且这些静态成员是在这个 cpp 文件中的类定义旁边定义的。然后这些静态成员获得外部链接(作为 GLOBAL 存在于相应 .o 文件的符号表中)。

现在您有另一个 cpp 文件,并且您想为自己的目的创建另一个 SomeClass(与第一个 cpp 文件中的那个无关,您甚至可能不知道第一个 cpp 文件中存在第一个 SomeClass)再次没有匿名命名空间。并且您定义了一个与第一个 SomeClass 的静态成员同名的静态成员。你在这里:你最终会出现联系冲突。

所以第一个 cpp 文件的作者应该将第一个 SameClass 隐藏在一个匿名的命名空间中,因为该类显然应该是一个实现细节,而不是一个被其他人重用的类。所以它的静态成员不应该有外部链接。

所以总的来说,我想说的是类的非 constexpr/非内联静态成员就像全局变量或非内联函数一样。因此,它们的链接可以使用匿名命名空间在内部进行,就像可以使用静态关键字或匿名命名空间对全局变量和函数进行链接一样。

// .cpp file
namespace 
{
    struct A
    {
        static int i;
    };
}

int A::i;
于 2020-02-24T09:37:54.060 回答