赞
踩
namespaces
namespaces
指的是标志符的某种可见范围。namespaces
是一个声明性区域,为内部的标志符提供一个访问namespaces
内的所有标识符彼此可见,没有任何限制。namespaces
具有扩展开放性,可以出现在任何模块文件中
namespaces
来定义一些组件,而他们可以散布于多个实质模块上std
的namespaces中1、第一种方法:直接指定标识符
std::cout << "hello namespaces" << std::endl;
2、第二种方法:使用using declaration
using std::cout;
using std::endl;
cout << "hello namespaces" << endl;
3、第三种方法:使用using directive
using directive
。因为它让xxx内定义的所有标识符都曝光了 using namespace std;
cout << "hello namespaces" << endl;
对于自定义命名空间,如何
namespace ContosoData { class ObjectManager{ public: void DoSomething(){} }; void func(ObjectManager){} } // 方法一:使用完全限定名 void func(){ ContosoData::ObjectManager mgr; mgr.DoSomething(); ContosoData::func(mgr); } // 方法二:使用using声明,以将一个标识符引入范围 void func1(){ using ContosoData::ObjectManager; ObjectManager mgr; mgr.DoSomething(); func(mgr); } // 方法三:使用using指令,以将命名空间中的所有内容引入范围 void func2(){ using namespace ContosoData; ObjectManager mgr; mgr.DoSomething(); func(mgr); }
一般情况下,避免将 using 指令(比如using namespace ContosoData
)放置在头文件 (*.h) 中,因为任何包含该标头的文件都会将命名空间中的所有内容引入范围,这将导致非常难以调试的名称隐藏和名称冲突问题。 在头文件中,始终使用完全限定名。 如果这些名称太长,可以使用命名空间别名将其缩短。
当你采用不同的模块和程序库时,经常会出现冲突现象,这是因为不同的模块和程序库可能针对不同的对象使用相同的标志符。 namespaces
(命名空间)可以解决这个问题
#include <iostream> using namespace std; namespace data { int a; int b; int numa(int a, int b); //函数声明 int add(int a, int b) { return a - b; } } int add(int a, int b) { return a + b; } //注意:必须在命名空间内部声明函数才能在外部定义这个函数实体,或者:“numa” : 不是“data”的成员 int data::numa(int a, int b) //::指明这个函数是命名空间里的函数实体 { return a * b; } int main() { cout << add(10, 29) << endl; //39 cout << data::add(10, 29) << endl; //::指明使用命名空间里面的函数 cout << data::numa(10, 29) << endl; }
总结:
#include <iostream> using namespace std; namespace V2014 { void fun(int num) { cout << "int " << "V2014" << endl; } } namespace V2015 { void fun(int num) { cout << "int " << "V2014" << endl; } void fun(double num) //重载 { cout << "double " << "V2014" << endl; } } int main() { V2014::fun(11); V2015::fun(11); V2015::fun(11.12); }
总结:
如果有新的需求又不打算对代码大改,可以对命名空间进行扩展
#include <iostream> using namespace std; namespace data { int a; int b; int numa(int a, int b); //函数声明 } int data::numa(int a, int b) //::指明这个函数是命名空间里的函数实体 { return a * b; } //如果有新的需求又不打算对代码大改,可以对命名空间进行扩展 namespace data { int add(int a, int b); //函数声明 } int data::add(int a, int b) { return a + b; } void main() { cout << data::numa(10, 29) << endl; cout << data::add(10, 29) << endl; cin.get(); }
可以创建显式命名空间,但不为其提供一个名称:
namespace
{
int MyFunc(){}
}
这称为未命名或匿名命名空间,当你想要使变量声明对其他文件中的代码不可见时,这会很有用, (也就是说,无需创建命名命名空间即可) 提供内部链接。同一文件中的所有代码都可以看到未命名的命名空间中的标识符,但这些标识符以及命名空间本身在该文件外部(或更准确地说,在翻译单元外部)不可见。
namespace ContosoDataServer
{
void Foo();
namespace Details
{
int CountImpl;
void Ban() { return Foo(); }
}
int Bar(){...};
int Baz(int i) { return Details::CountImpl; }
}
我们知道, 命名空间可以嵌套使用:
#include <iostream> namespace Jim { namespace Basic { struct Knife{ Knife(){printf("Knife in Basic");} }; class CorkScrew{}; }; namespace TookKit{ template<typename T> class SwissArmyKnife{}; }; namespace Other{ // Knife a; struct Knife{ Knife(){printf("Knife in Other");} }; Knife c; //"Knife in Other" Basic::Knife k; //"Knife in Basic" } }; int main(){ Jim::TookKit::SwissArmyKnife<Jim::Basic::Knife> swissArmyKnife; }
但是这样可能会带来一个问题,类型声明Jim::TookKit::SwissArmyKnife<Jim::Basic::Knife>
太长了,其实没有必要让用户看到子名字空间,因此可以像这样改写代码:
#include <iostream> namespace Jim { namespace Basic { struct Knife{ Knife(){printf("Knife in Basic");} }; class CorkScrew{}; }; namespace TookKit{ template<typename T> class SwissArmyKnife{}; }; namespace Other{ // Knife a; struct Knife{ Knife(){printf("Knife in Other");} }; Knife c; //"Knife in Other" Basic::Knife k; //"Knife in Basic" } using namespace Basic; using namespace TookKit; }; // 这是LiLei再使用Jim的库 namespace Jim{ template<> class SwissArmyKnife<Knife>{}; // 编译失败 }; using namespace Jim; int main(){ SwissArmyKnife<Knife> swissArmyKnife; }
上面的改写简化了名字的编写。但是带来了一个新的问题:库的使用者觉得TookKit中的模板SwissArmyKnife不合用,因此决定特化一个SwissArmyKnife< Knife>的版本。这时,编译无法通过。这是由于C++不允许在不同的名字空间中对模板进行特化造成的。
为此,C++11中引入一个叫做内联的命名空间的新特性。内联命名空间允许在父名字空间中定义或者特化子名字空间的模板,即:
#include <iostream> namespace Jim { namespace Basic { struct Knife{ Knife(){printf("Knife in Basic");} }; class CorkScrew{}; }; inline namespace TookKit{ template<typename T> class SwissArmyKnife{}; }; namespace Other{ // Knife a; struct Knife{ Knife(){printf("Knife in Other");} }; Knife c; //"Knife in Other" Basic::Knife k; //"Knife in Basic" } using namespace Basic; using namespace TookKit; }; // 这是LiLei再使用Jim的库 namespace Jim{ template<> class SwissArmyKnife<Knife>{}; //ok }; using namespace Jim; int main(){ SwissArmyKnife<Knife> swissArmyKnife; }
与普通嵌套命名空间不同,内联命名空间的成员会被视为父命名空间的成员。
//Header.h #include <string> namespace Test { namespace old_ns { std::string Func() { return std::string("Hello from old"); } } inline namespace new_ns // inline标示默认的版本 { std::string Func() { return std::string("Hello from new"); } } } #include "header.h" #include <string> #include <iostream> int main() { using namespace Test; std::string s = Func(); printf("%s", s.c_str()); // "Hello from new" return 0; }
namespace Parent
{
inline namespace new_ns
{
template <typename T>
struct C
{
T member;
};
}
template<>
class C<int> {};
}
可以将内联命名空间用作版本控制机制,以管理对库的公共接口的更改。 例如,可以创建单个父命名空间,并将接口的每个版本封装到嵌套在父命名空间内的其自己的命名空间中。 保留最新或首选的版本的命名空间限定为内联,并因此以父命名空间的直接成员的形式公开。 调用 Parent::Class 的客户端代码将自动绑定到新代码。 通过使用指向包含该代码的嵌套命名空间的完全限定路径,选择使用较旧版本的客户端仍可以对其进行访问。
#include <iostream> using namespace std; namespace all { namespace V2014 { void fun(int num) { cout << "int " << "V2014" << endl; } } } namespace all { inline namespace V2015 //默认的版本 { void fun(int num) { cout << "int " << "V2015" << endl; } void fun(double num) //重载 { cout << "double " << "V2015" << endl; } } } int main() { all::fun(11); //int 2015 }
下面的示例演示一个接口的两个版本,每个版本位于一个嵌套命名空间中。 通过 v_10 接口对 v_20 命名空间进行了某些修改,且该命名空间被标记为内联。 使用新库并调用 Contoso::Funcs::Add 的客户端代码将调用 v_20 版本。 尝试调用 Contoso::Funcs::Divide 的代码现在将获取一个编译时错误。 如果它们确实需要该函数,则仍可以通过显式调用Contoso::v_10::Funcs::Divide
访问 v_10 版本。
namespace Contoso { namespace v_10 { template <typename T> class Funcs { public: Funcs(void); T Add(T a, T b); T Subtract(T a, T b); T Multiply(T a, T b); T Divide(T a, T b); }; } inline namespace v_20 { template <typename T> class Funcs { public: Funcs(void); T Add(T a, T b); T Subtract(T a, T b); T Multiply(T a, T b); std::vector<double> Log(double); T Accumulate(std::vector<T> nums); }; } }
内联命名空间可以跟宏__cplusplus
结合使用,编译器就可以自动根据编译器选择默认的版本了:
#include <iostream> namespace Jim { #if __cplusplus == 201103L inline #endif namespace cpp11{ struct Knife{ Knife(){printf("Knife in C++11");}}; }; #if __cplusplus < 201103L inline #endif namespace oldcpp{ struct Knife{ Knife(){printf("Knife aa C++11");}}; }; };
内联命名空间会自动把内部的标识符放到外层作用域,比如:
namespace X {
inline namespace Y {
void foo();
} // namespace Y
} // namespace X
X::Y::foo() 与 X::foo() 彼此可代替。内联命名空间主要用来保持跨版本的 ABI 兼容性。内联命名空间只在大型版本控制里有用。
命名空间名称必须是唯一的,这意味着通常它们不应太短。 如果名称的长度使代码难以读取,则可以创建一个命名空间别名作为实际名称的缩写。 例如:
namespace a_very_long_namespace_name { class Foo {}; }
namespace AVLNN = a_very_long_namespace_name;
void Bar(AVLNN::Foo foo){ }
通常情况下,在头文件中声明一个命名空间。如果函数实现位于一个单独的文件中,则限定函数名称:
//contosoData.h
#pragma once
namespace ContosoDataServer
{
void Foo();
int Bar();
}
//Contosodata 中的函数实现应使用完全限定的名称,即使你将 using 指令放在文件顶部:
#include "contosodata.h"
using namespace ContosoDataServer;
void ContosoDataServer::Foo() // use fully-qualified name here
{
// no qualification needed for Bar()
Bar();
}
int ContosoDataServer::Bar(){return 0;}
指定的命名空间的成员可以在定义的名称的显式限定所声明的命名空间的外部进行定义。 但是,定义必须出现在命名空间中的声明位置之后,该命名空间包含在声明的命名空间中。 例如:
// defining_namespace_members.cpp
// C2039 expected
namespace V {
void f();
}
void V::f() { } // ok
void V::g() { } // C2039, g() is not yet a member of V
namespace V {
void g();
}
声明的形式:
using namespace_name::name
一个using声明一次只引入一个命名空间成员。
using std::cout;
using std::vector;
int main()
{
int x;
cin>>x;//wrong
std::cin>>x;//right
cout<<x;//right
}
using声明中引入的名字遵循常规作用域规则:从using声明点开始,直到包含该using声明的作用域的末尾,名字都是可见的。外部作用域中定义的同名实体被屏蔽。
注意using声明出现的位置的影响,如例1和例2
--example 1-- #include<iostream> using namespace std; namespace Lib { void print(int x) { cout<<"int"<<x<<endl; } } void print(double y){ cout<<"double"<<y<<endl; } int main() { using Lib::print;//example 1 : main作用域中嵌套了Lib命名空间 print(1.3); print(3); getchar(); return 1; } //输出: int 1 int 3
--example 2-- #include<iostream> using namespace std; namespace Lib { void print(int x) { cout<<"int"<<x<<endl; } } void print(double y){ cout<<"double"<<y<<endl; } using Lib::print;//example 2 : main作用域中平行于Lib命名空间 int main() { print(1.3); print(3); getchar(); return 1; } //输出: double 1.3 int 3
形式:
using namespace 命名空间名
using指示同using声明一样,可以使我们能够使用命名空间的简写形式,简写名字从using指示点开始,直到出现using指示的作用域的末尾。但不同的是using声明可以选择性的部分可见,但using指示使得特定命名空间名的所有可见。
例1:
namespace A
{
int i,j;
}
void f(){
using namespace A;//using指示
cout<<i*j<<endl;
}
namespace {
...
} // namespace
/ 禁止 —— 污染命名空间
using namespace foo;
namespace X {
inline namespace Y {
void foo();
} // namespace Y
} // namespace X
// .h 文件
namespace mynamespace {
// 所有声明都置于命名空间中
// 注意不要使用缩进
class MyClass {
public:
...
void Foo();
};
} // namespace mynamespace
// .cc 文件
namespace mynamespace {
// 函数定义都置于命名空间中
void MyClass::Foo() {
...
}
} // namespace mynamespace
// 在 .cc 中使用别名缩短常用的命名空间
namespace baz = ::foo::bar::baz;
// 在 .h 中使用别名缩短常用的命名空间
namespace librarian {
namespace impl { // 仅限内部使用
namespace sidetable = ::pipeline_diagnostics::sidetable;
} // namespace impl
inline void my_inline_function() {
// 限制在一个函数中的命名空间别名
namespace baz = ::foo::bar::baz;
...
}
} // namespace librarian
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。