C++ using namespace std 详解 与 命名空间的使用

所谓namespace,是指标识符的各种可见范围。C++标准程序库中的所有标识符都被定义于一个名为std的namespace中。 一 : <iostream>和<iostream.h>是不一样,前者没有后缀,实际上,在你的编译器include文件夹里面可以看到,二者是两个文件,打开文件就会发现,里面的代码是不一样的。 后缀为.h的头文件c++标准已经明确提出不支持了,早些的实现将标准库功能定义在全局空间里,声明在带.h后缀的头文件里,c++标准为了和C区别开,也为了正确使用命名空间,规定头文件不使用后缀.h。 因此,当使用<iostream.h>时,相当于在c中调用库函数,使用的是全局命名空间,也就是早期的c++实现;当使用<iostream>的时候,该头文件没有定义全局命名空间,必须使用namespace std;这样才能正确使用cout。 二: 所谓namespace,是指标识符的各种可见范围。 C++标准程序库中的所有标识符都被定义于一个名为std的namespace中。 由于namespace的概念,使用C++标准程序库的任何标识符时,可以有三种选择: 1、直接指定标识符。例如std::ostream而不是ostream。完整语句如下: std::cout << std::hex << 3.4 << std::endl; 2、使用using关键字。 using std::cout; using std::endl; 以上程序可以写成 cout << std::hex << 3.4 << endl; 3、最方便的就是使用using namespace std; 例如: #include <iostream> #include <sstream> #include <string> using namespace std; 这样命名空间std内定义的所有标识符都有效(曝光)。就好像它们被声明为全局变量一样。那么以上语句可以如下写: cout << hex << 3.4 << endl; 因为标准库非常的庞大,所程序员在选择的类的名称或函数名时就很有可能和标准库中的某个名字相同。所以为了避免这种情况所造成的名字冲突,就把标准库中的一切都被放在名字空间std中。但这又会带来了一个新问题。无数原有的C++代码都依赖于使用了多年的伪标准库中的功能,他们都是在全局空间下的。            所以就有了<iostream.h>和<iostream>等等这样的头文件,一个是为了兼容以前的C++代码,一个是为了支持新的标准。 命名空间std封装的是标准程序库的名称,标准程序库为了和以前的头文件区别,一般不加".h"               C++命名空间 的使用方法 C++语言提供一个全局的命名空间namespace,可以避免导致全局命名冲突问题。举一个实例,请注意以下两个头文件: // one.h char func(char); class String { ... }; // somelib.h class String { ... }; 如果按照上述方式定义,那么这两个头文件不可能包含在同一个程序中,因为String类会发生冲突。 所谓命名空间,是一种将程序库名称封装起来的方法,它就像在各个程序库中立起一道道围墙。比如:

namespace myown1{

string user_name = "myown1 "; } namespace myown2{

string user_name = "myown2 "; }

int main() { cout < < "/n " < < "Hello, " < < myown1::user_name //用命名空间限制符myown1访问变量user_name < < "... and goodbye!/n ";

cout < < "/n " < < "Hello, " < < myown2::user_name //用命名空间限制符myown2访问变量user_name < < "... and goodbye!/n ";

return 0; }

 

无名命名空间 标准C++引入命名空间,除了可以避免成员的名称发生冲突之外,还可以使代码保持局部性,从而保护代码不被他人非法使用。如果你的目的主要是后者,而且又为替命名空间取一个好听、有意义、且与别人的命名空间不重名的名称而烦恼的话,标准C++还允许你定义一个无名命名空间。你可以在当前编译单元中(无名命名空间之外),直接使用无名命名空间中的成员名称,但是在当前编译单元之外,它又是不可见的。 无名命名空间的定义格式为: namespace { 声明序列可选 } 实际上,上面的定义等价于:(标准C++中有一个隐含的使用指令) namespace $$$ { 声明序列可选 } using namespace $$$; 例如: namespace { int i; void f() {/*……*/} } int main() { i = 0; // 可直接使用无名命名空间中的成员i f(); // 可直接使用无名命名空间中的成员f()

现在就算在同一个程序中使用String类也不会发生冲突了,因为他们分别变成了:one::String()以及Somelib::String() 这样,就可以通过声明命名空间来区分不同的类或函数等了。

 

比如C++标准库定义了命名空间:std,其中包含容器vector,示例如下: #include "stdafx.h" #include <vector> #include <iostream> #include <algorithm> using namespace std; int main(int argc, char* argv[]) { const int arraysize = 7; int ia[arraysize] = {0,1,2,3,4,5}; file://定义容器vector vector<int> ivect(ia,ia+arraysize); vector<int>::iterator it1 = find(ivect.begin (),ivect.end (),4); if(it1 == ivect.end ())   cout<<"4 not found "<<endl; else   cout<<"4 found "<<*it1<<endl; return 0; } 输出结果为:4 found 4.

在C++中,名称(name)可以是符号常量、变量、宏、函数、结构、枚举、类和对象等等。为了避免,在大规模程序的设计中,以及在程序员使用各种各样的C++库时,这些标识符的命名发生冲突,标准C++引入了关键字namespace(命名空间/名字空间/名称空间/名域),可以更好地控制标识符的作用域。  MFC中并没有使用命名空间,但是在.NET框架、MC++和C++/CLI中,都大量使用了命名空间。 1)作用域与命名空间 l 相关概念 与命名空间相关的概念有: n 声明域(declaration region)—— 声明标识符的区域。如在函数外面声明的全局变量,它的声明域为声明所在的文件。在函数内声明的局部变量,它的声明域为声明所在的代码块(例如整个函数体或整个复合语句)。 n 潜在作用域(potential scope)—— 从声明点开始,到声明域的末尾的区域。因为C++采用的是先声明后使用的原则,所以在声明点之前的声明域中,标识符是不能用的。即,标识符的潜在作用域,一般会小于其声明域。 n 作用域(scope)—— 标识符对程序可见的范围。标识符在其潜在作用域内,并非在任何地方都是可见的。例如,局部变量可以屏蔽全局变量、嵌套层次中的内层变量可以屏蔽外层变量,从而被屏蔽的全局或外层变量在其倍屏蔽的区域内是不可见的。所以,一个标识符的作用域可能小于其潜在作用域。 命名空间 命名空间(namespace)是一种描述逻辑分组的机制,可以将按某些标准在逻辑上属于同一个集团的声明放在同一个命名空间中。 原来C++标识符的作用域分成三级:代码块({……},如复合语句和函数体)、类和全局。现在,在其中的类和全局之间,标准C++又添加了命名空间这一个作用域级别。 命名空间可以是全局的,也可以位于另一个命名空间之中,但是不能位于类和代码块中。所以,在命名空间中声明的名称(标识符),默认具有外部链接特性(除非它引用了常量)。 在所有命名空间之外,还存在一个全局命名空间,它对应于文件级的声明域。因此,在命名空间机制中,原来的全局变量,现在被认为位于全局命名空间中。 标准C++库(不包括标准C库)中所包含的所有内容(包括常量、变量、结构、类和函数等)都被定义在命名空间std(standard标准)中了。 2)定义命名空间 有两种形式的命名空间——有名的和无名的。 命名空间的定义格式为:(取自C++标准文档) named-namespace-definition: namespace identifier { namespace-body } unnamed-namespace-definition: namespace { namespace-body } namespace-body: declaration-seqopt 即:(自己翻译并改写的) 有名的命名空间: namespace 命名空间名 { 声明序列可选 } 无名的命名空间: namespace { 声明序列可选 } 命名空间的成员,是在命名空间定义中的花括号内声明了的名称。可以在命名空间的定义内,定义命名空间的成员(内部定义)。也可以只在命名空间的定义内声明成员,而在命名空间的定义之外,定义命名空间的成员(外部定义)。 命名空间成员的外部定义的格式为: 命名空间名::成员名 …… 例如: // out.h namespace Outer { // 命名空间Outer的定义     int i; // 命名空间Outer的成员i的内部定义     namespace Inner { // 子命名空间Inner的内部定义          void f() { i++; } // 命名空间Inner的成员f()的内部定义,其中的i为Outer::i          int i;          void g() { i++; } // 命名空间Inner的成员g()的内部定义,其中的i为Inner::i          void h(); // 命名空间Inner的成员h()的声明     }     void f(); // 命名空间Outer的成员f()的声明     // namespace Inner2; // 错误,不能声明子命名空间 } void Outer::f() {i--;} // 命名空间Outer的成员f()的外部定义 void Outer::Inner::h() {i--;} // 命名空间Inner的成员h()的外部定义 // namespace Outer::Inner2 {/*……*/} // 错误,不能在外部定义子命名空间 注意: 不能在命名空间的定义中声明(另一个嵌套的)子命名空间,只能在命名空间的定义中定义子命名空间。 也不能直接使用“命名空间名::成员名 ……”定义方式,为命名空间添加新成员,而必须先在命名空间的定义中添加新成员的声明。 另外,命名空间是开放的,即可以随时把新的成员名称加入到已有的命名空间之中去。方法是,多次声明和定义同一命名空间,每次添加自己的新成员和名称。例如: namespace A { int i; void f(); } // 现在A有成员i和f() namespace A { int j; void g(); } // 现在A有成员i、f()、j和g() 还可以用多种方法,来组合现有的命名空间,让它们为我所用。例如: namespace My_lib { using namespace His_string; using namespace Her_vector; using Your_list::List; void my_f(String &, List &); } …… using namespace My_lib; …… Vector vs[5]; List li[10]; my_f(vs[2], li[5]); 3)使用命名空间 l 作用域解析运算符(::) 对命名空间中成员的引用,需要使用命名空间的作用域解析运算符::。例如: // out1.cpp #include "out.h" #include  int main ( ) { Outer::i = 0; Outer::f(); // Outer::i = -1; Outer::Inner::f(); // Outer::i = 0; Outer::Inner::i = 0; Outer::Inner::g(); // Inner::i = 1; Outer::Inner::h(); // Inner::i = 0; std::cout << "Hello, World!" << std::endl; std::cout << "Outer::i = " << Outer::i << ", Inner::i = " << Outer::Inner::i << std::endl; } l using指令(using namespace) 为了省去每次调用Inner成员和标准库的函数和对象时,都要添加Outer::Inner::和sta::的麻烦,可以使用标准C++的using编译指令来简化对命名空间中的名称的使用。格式为: using namespace 命名空间名[::命名空间名……]; 在这条语句之后,就可以直接使用该命名空间中的标识符,而不必写前面的命名空间定位部分。因为using指令,使所指定的整个命名空间中的所有成员都直接可用。例如: // out2.cpp #include "out.h" #include  // using namespace Outer; // 编译错误,因为变量i和函数f()有名称冲突 using namespace Outer::Inner; using namespace std; int main ( ) { Outer::i = 0; Outer::f(); // Outer::i = -1; f(); // Inner::f(),Outer::i = 0; i = 0; // Inner::i g(); // Inner::g(),Inner::i = 1; h(); // Inner::h(),Inner::i = 0; cout << "Hello, World!" << endl; cout << "Outer::i = " << Outer::i << ", Inner::i = " << i << endl; } 又例如:(.NET框架) using namespace System::Drawing::Imaging; using namespace System::Window::Forms::Design::Behavior; l using声明(using) 除了可以使用using编译指令(组合关键字using namespace)外,还可以使用using声明来简化对命名空间中的名称的使用。格式为: using 命名空间名::[命名空间名::……]成员名; 注意,关键字using后面并没有跟关键字namespace,而且最后必须为命名空间的成员名(而在using编译指令的最后,必须为命名空间名)。 与using指令不同的是,using声明只是把命名空间的特定成员的名称,添加该声明所在的区域中,使得该成员可以不需要采用,(多级)命名空间的作用域解析运算符来定位,而直接被使用。但是该命名空间的其他成员,仍然需要作用域解析运算符来定位。例如: // out3.cpp #include "out.h" #include  using namespace Outer; // 注意,此处无::Inner using namespace std; // using Inner::f; // 编译错误,因为函数f()有名称冲突 using Inner::g; // 此处省去Outer::,是因为Outer已经被前面的using指令作用过了 using Inner::h; int main ( ) { i = 0; // Outer::i f(); // Outer::f(),Outer::i = -1; Inner::f(); // Outer::i = 0; Inner::i = 0; g(); // Inner::g(),Inner::i = 1; h(); // Inner::h(),Inner::i = 0; cout << "Hello, World!" << endl; cout << "Outer::i = " << i << ", Inner::i = " << Inner::i << endl; } l using指令与using声明的比较 可见,using编译指令和using声明,都可以简化对命名空间中名称的访问。 using指令使用后,可以一劳永逸,对整个命名空间的所有成员都有效,非常方便。而using声明,则必须对命名空间的不同成员名称,一个一个地去声明,非常麻烦。 但是,一般来说,使用using声明会更安全。因为,using声明只导入指定的名称,如果该名称与局部名称发生冲突,编译器会报错。而using指令导入整个命名空间中的所有成员的名称,包括那些可能根本用不到的名称,如果其中有名称与局部名称发生冲突,则编译器并不会发出任何警告信息,而只是用局部名去自动覆盖命名空间中的同名成员。特别是命名空间的开放性,使得一个命名空间的成员,可能分散在多个地方,程序员难以准确知道,别人到底为该命名空间添加了哪些名称。 虽然使用命名空间的方法,有多种可供选择。但是不能贪图方便,一味使用using 指令,这样就完全背离了设计命名空间的初衷,也失去了命名空间应该具有的防止名称冲突的功能。 一般情况下,对偶尔使用的命名空间成员,应该使用命名空间的作用域解析运算符来直接给名称定位。而对一个大命名空间中的经常要使用的少数几个成员,提倡使用using声明,而不应该使用using编译指令。只有需要反复使用同一个命名空间的许多数成员时,使用using编译指令,才被认为是可取的。 例如,如果一个程序(如上面的outi.cpp)只使用一两次cout,而且也不使用std命名空间中的其他成员,则可以使用命名空间的作用域解析运算符来直接定位。如: #include  …… std::cout << "Hello, World!" << std::endl; std::cout << "Outer::i = " << Outer::i << ", Inner::i = " << Outer::Inner::i << std::endl; 又例如,如果一个程序要反复使用std命名空间中的cin、cout和cerr(如上面的outi.cpp),而不怎么使用其他std命名空间中的其他成员,则应该使用using 声明而不是using指令。如: #include  …… using std::cout; cout << "Hello, World!" << endl; cout << "Outer::i = " << Outer::i << ", Inner::i = " << Outer::Inner::i << endl; 4)命名空间的名称 l 命名空间别名 标准C++引入命名空间,主要是为了避免成员的名称冲突。若果用户都给自己的命名空间取简短的名称,那么这些(往往同是全局级的)命名空间本身,也可能发生名称冲突。如果为了避免冲突,而为命名空间取很长的名称,则使用起来就会不方便。这是一个典型的两难问题。 标准C++为此提供了一种解决方案——命名空间别名,格式为: namespace 别名 = 命名空间名; 例如:(AT&T美国电话电报公司) namespace American_Telephone_and_Telegraph { // 命名空间名太长 class String { String(const char*); // …… } } American_Telephone_and_Telegraph::String s1 // 使用不方便 = new American_Telephone_and_Telegraph::String("Grieg"); namespace ATT = American_Telephone_and_Telegraph; // 定义别名 ATT::String s2 = new ATT::String("Bush"); // 使用方便 ATT::String s3 = new ATT::String("Nielsen"); l 无名命名空间 标准C++引入命名空间,除了可以避免成员的名称发生冲突之外,还可以使代码保持局部性,从而保护代码不被他人非法使用。如果你的目的主要是后者,而且又为替命名空间取一个好听、有意义、且与别人的命名空间不重名的名称而烦恼的话,标准C++还允许你定义一个无名命名空间。你可以在当前编译单元中(无名命名空间之外),直接使用无名命名空间中的成员名称,但是在当前编译单元之外,它又是不可见的。 无名命名空间的定义格式为: namespace { 声明序列可选 } 实际上,上面的定义等价于:(标准C++中有一个隐含的使用指令) namespace $$$ { 声明序列可选 } using namespace $$$; 例如: namespace { int i; void f() {/*……*/} } int main() { i = 0; // 可直接使用无名命名空间中的成员i f(); // 可直接使用无名命名空间中的成员f() }
  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答: 在C++中,命名空间(namespace)是一种用来避免名称冲突的机制。当工程越大,使用多个厂商的类库时,名称冲突的可能性就越大。为了避免这种冲突,C++引入了命名空间的概念。命名空间可以将一组相关的标识符包装在一个作用域内,从而限制其在程序中的可见性。通过使用命名空间,可以更好地控制标识符的作用域,避免名称冲突。\[2\] 在C++中,可以使用命名空间嵌套来组织代码。当命名空间嵌套层次太多时,使用其中的成员会变得很麻烦。为了方便使用,可以对嵌套的命名空间取别名。通过使用别名,可以简化代码中对命名空间成员的访问。例如,在代码中可以使用"namespace alias_kzl = kxl::kdl::kzl;"来为"kxl::kdl::kzl"命名空间取别名为"alias_kzl"。然后可以通过"alias_kzl::y"来访问"kxl::kdl::kzl"命名空间中的"y"成员。\[1\] 在使用命名空间时,有多种方式可以引入命名空间中的成员。一种常见的方式是使用"using"关键字,例如"using namespace std;"可以将"std"命名空间中的成员引入当前作用域。然而,这种方式可能会导致名称冲突和不明确的代码。因此,一种更好的做法是使用限定符来访问命名空间中的成员,例如"std::cout"。这样可以明确指定使用的是哪个命名空间中的成员,避免冲突和歧义。\[3\] #### 引用[.reference_title] - *1* [C++ namespace](https://blog.csdn.net/kjl167/article/details/126244107)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [C++ namespace用法详细介绍](https://blog.csdn.net/Yifancoder/article/details/124127828)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [C++ namespace详解](https://blog.csdn.net/weixin_30808575/article/details/96584787)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值