我有Java背景,使用的是包,而不是名称空间。我习惯于将一起工作以形成完整对象的类放入包中,然后从该包中重用它们。但是现在我在用c++工作。

在c++中如何使用名称空间?您是为整个应用程序创建单个名称空间,还是为主要组件创建名称空间?如果是,如何从其他名称空间中的类创建对象?


当前回答

Vincent Robert在他的评论中是正确的,你如何在c++中正确使用名称空间?

使用命名空间

命名空间的使用至少有助于避免名称冲突。在Java中,这是通过“org。域名”的成语(因为它假定一个人不会使用他/她自己的域名以外的任何东西)。

在c++中,您可以为模块中的所有代码指定一个名称空间。例如,对于模块MyModule.dll,可以将其代码命名为MyModule。我在其他地方看到有人使用MyCompany::MyProject::MyModule。我想这有点过分了,但总的来说,在我看来这是正确的。

使用“使用”

Using应该非常谨慎地使用,因为它有效地将一个(或所有)符号从名称空间导入到当前名称空间。

在头文件中这样做是邪恶的,因为你的头文件会污染包括它在内的所有源代码(这让我想起了宏…),甚至在源文件中,在函数作用域之外也是糟糕的风格,因为它会在全局作用域导入命名空间中的符号。

使用"using"最安全的方法是导入选定的符号:

void doSomething()
{
   using std::string ; // string is now "imported", at least,
                       // until the end of the function
   string a("Hello World!") ;
   std::cout << a << std::endl ;
}

void doSomethingElse()
{
   using namespace std ; // everything from std is now "imported", at least,
                       // until the end of the function
   string a("Hello World!") ;
   cout << a << endl ;
}

您将在教程或示例代码中看到大量“使用名称空间std;”。原因是为了减少符号的数量,使阅读更容易,而不是因为这是一个好主意。

Scott Meyers不鼓励“使用命名空间std;”(我不记得具体是哪本书了,但如果有必要,我可以找到它)。

名称空间组成

名称空间不仅仅是包。另一个例子可以在Bjarne Stroustrup的“c++编程语言”中找到。

在“特别版”中,在8.2.8命名空间组合中,他描述了如何将两个命名空间AAA和BBB合并为另一个名为CCC的命名空间。因此CCC成为AAA和BBB的别名:

namespace AAA
{
   void doSomething() ;
}

namespace BBB
{
   void doSomethingElse() ;
}

namespace CCC
{
   using namespace AAA ;
   using namespace BBB ;
}

void doSomethingAgain()
{
   CCC::doSomething() ;
   CCC::doSomethingElse() ;
}

您甚至可以从不同的名称空间导入选择的符号,以构建您自己的自定义名称空间接口。我还没有找到它的实际用途,但在理论上,它很酷。

其他回答

@marius

是的,你可以同时使用几个命名空间,例如:

using namespace boost;   
using namespace std;  

shared_ptr<int> p(new int(1));   // shared_ptr belongs to boost   
cout << "cout belongs to std::" << endl;   // cout and endl are in std

[2014年2月-(真的有那么长时间吗?):正如乔伊在下面指出的,这个特殊的例子现在是模棱两可的。Boost和std::现在各有一个shared_ptr.]

注意,c++中的命名空间实际上只是一个名称空间。它们不提供Java包所提供的任何封装,因此您可能不会经常使用它们。

不要相信每个人都说名称空间只是名称空间。

它们很重要,因为它们被编译器用来应用接口原则。基本上,可以用一个例子来解释:

namespace ns {

class A
{
};

void print(A a)
{
}

}

如果你想打印一个A对象,代码将是这样的:

ns::A a;
print(a);

注意,在调用函数时没有显式地提到名称空间。这是接口原则:c++将一个以类型作为参数的函数视为该类型接口的一部分,因此不需要指定命名空间,因为形参已经隐含了命名空间。

为什么这个原则很重要呢?假设类A的作者没有为这个类提供print()函数。你必须自己提供一份。作为一名优秀的程序员,您将在自己的名称空间中定义这个函数,或者可能在全局名称空间中定义。

namespace ns {

class A
{
};

}

void print(A a)
{
}

你的代码可以在任何你想要的地方调用print(a)函数。现在想象一下,多年以后,作者决定提供一个print()函数,它比你的更好,因为他知道他的类的内部结构,并且可以提供一个比你的更好的版本。

然后,c++作者决定使用他的print()函数版本,而不是在另一个名称空间中提供的函数,以尊重接口原则。print()函数的“升级”应该尽可能简单,这意味着您不必更改对print()函数的每次调用。这就是为什么在c++中不需要指定命名空间就可以调用“接口函数”(与类在同一命名空间中的函数)。

这就是为什么在使用c++名称空间时应该将其视为“接口”,并记住接口原则。

如果你想要更好地解释这种行为,你可以参考Herb Sutter的《例外c++》这本书

另外,请注意,您可以添加到名称空间。举个例子就更清楚了,我的意思是你可以有:

namespace MyNamespace
{
    double square(double x) { return x * x; }
}

在文件square.h中,和

namespace MyNamespace
{
    double cube(double x) { return x * x * x; }
}

在文件cube.h中。这定义了一个单独的名称空间MyNamespace(也就是说,您可以跨多个文件定义一个单独的名称空间)。

我更喜欢为应用程序使用顶级名称空间,而为组件使用子名称空间。

使用来自其他名称空间的类的方式与java中的方式惊人地相似。 你可以使用“use NAMESPACE”,这类似于“import PACKAGE”语句,例如使用std。或者你指定包作为类的前缀,用“::”分隔,例如std::string。这类似于“java.lang”。字符串”。