为什么使用未命名的命名空间以及它们的好处是什么?

时间:2008-12-10 20:03:06

标签: c++ oop namespaces

我刚刚加入了一个新的C ++软件项目,我正在尝试理解这个设计。该项目经常使用未命名的命名空间。例如,类定义文件中可能出现类似这样的事情:

// newusertype.cc
namespace {
  const int SIZE_OF_ARRAY_X;
  const int SIZE_OF_ARRAY_Y;
  bool getState(userType*,otherUserType*);
}

newusertype::newusertype(...) {...

可能导致人们使用未命名的命名空间的设计注意事项是什么?有哪些优点和缺点?

6 个答案:

答案 0 :(得分:169)

(在下文中,这些内容不再适用于C ++ 11,但确实适用于C ++ 03.C ++ 11几乎没有任何差异(如果有的话,它们只是语言)我不记得的律师差异)。

未命名的命名空间是使标识符转换单元成为本地的实用程序。它们的行为就像为命名空间选择每个翻译单元的唯一名称一样:

namespace unique { /* empty */ }
using namespace unique;
namespace unique { /* namespace body. stuff in here */ }

使用空体的额外步骤很重要,因此您可以在命名空间体内引用在该命名空间中定义的::name之类的标识符,因为using指令已经发生。

这意味着您可以拥有名为(例如)help的免费函数,这些函数可以存在于多个翻译单元中,并且它们不会在链接时发生冲突。效果几乎与使用C中使用的static关键字相同,您可以将其放入标识符声明中。未命名的命名空间是一种更好的替代方案,甚至可以将类型转换单元设置为本地。

namespace { int a1; }
static int a2;

两个a都是本地翻译单位,在链接时不会发生冲突。但不同之处在于匿名命名空间中的a1获得了唯一的名称。

阅读comeau-computing Why is an unnamed namespace used instead of static?Archive.org mirror)的优秀文章。

答案 1 :(得分:58)

在匿名命名空间中包含某些内容意味着它是translation unit(.cpp文件及其所有包含文件)的本地内容,这意味着如果在其他位置定义了具有相同名称的其他符号,则不会违反{ {3}}(ODR)。

这与具有静态全局变量或静态函数的C方式相同,但它也可以用于类定义(并且应该在C ++中使用而不是static)。

同一文件中的所有匿名命名空间都被视为相同的命名空间,不同文件中的所有匿名命名空间都是不同的。匿名命名空间相当于:

namespace __unique_compiler_generated_identifer0x42 {
    ...
}
using namespace __unique_compiler_generated_identifer0x42;

答案 2 :(得分:12)

该示例显示您加入的项目中的人员不了解匿名命名空间:)

namespace {
    const int SIZE_OF_ARRAY_X;
    const int SIZE_OF_ARRAY_Y;

这些不需要在匿名命名空间中,因为const对象已经具有静态链接,因此不可能与另一个翻译单元中的同名标识符冲突。

    bool getState(userType*,otherUserType*);
}

这实际上是一种悲观情绪:getState()有外部联系。通常更喜欢静态链接,因为它不会污染符号表。最好写

static bool getState(/*...*/);

这里。我陷入了同样的陷阱(标准中的措辞表明文件静态以某种方式被弃用以支持匿名命名空间),但是在像KDE这样的大型C ++项目中工作,你会让很多人以正确的方式转过头来再次:)

答案 3 :(得分:12)

除了这个问题的其他答案之外,使用匿名命名空间还可以提高性能。由于命名空间中的符号不​​需要任何外部链接,因此编译器可以更自由地对命名空间内的代码执行积极的优化。例如,可以内联在循环中多次调用一次的函数,而不会对代码大小产生任何影响。

例如,在我的系统上,如果使用匿名命名空间,则以下代码占用大约70%的运行时间(x86-64 gcc-4.6.3和-O2;请注意,add_val中的额外代码使编译器不能想要包括两次)。

#include <iostream>

namespace {
  double a;
  void b(double x)
  {
    a -= x;
  }
  void add_val(double x)
  {
    a += x;
    if(x==0.01) b(0);
    if(x==0.02) b(0.6);
    if(x==0.03) b(-0.1);
    if(x==0.04) b(0.4);
  }
}

int main()
{
  a = 0;
  for(int i=0; i<1000000000; ++i)
    {
      add_val(i*1e-10);
    }
  std::cout << a << '\n';
  return 0;
}

答案 4 :(得分:9)

匿名命名空间使得封闭的变量,函数,类等仅在该文件中可用。在您的示例中,它是一种避免全局变量的方法。没有运行时或编译时性能差异。

除了“我希望这个变量,函数,类等是公共的还是私有的?”之外,没有太多的优点或缺点。

答案 5 :(得分:6)

未命名的命名空间将类,变量,函数和对象的访问限制为定义它的文件。未命名的命名空间功能类似于C / C ++中的static关键字 static关键字将全局变量和函数的访问权限限制在定义它们的文件中 未命名的命名空间和static关键字之间存在差异,因为未命名的命名空间优于静态命名空间。 static关键字可以与变量,函数和对象一起使用,但不能与用户定义的类一起使用 例如:

static int x;  // Correct 

但是,

static class xyz {/*Body of class*/} //Wrong
static structure {/*Body of structure*/} //Wrong

但是使用未命名的命名空间也是可以的。 例如,

 namespace {
           class xyz {/*Body of class*/}
           static structure {/*Body of structure*/}
  } //Correct