C++ using namespace std 详解
所谓namespace,是指标识符的各种可见范围。C++标准程序库中的所有标识符都被定义于一个名为std的namespace中。
一:
<iostream>
和<iostream.h>
是不一样,前者没有后缀,实际上,在你的编译器include文件夹里面可以看到,二者是两个文件,打开文件就会发现,里面的代码是不一样的。
后缀为.h的头文件c++标准已经明确提出不支持了,早些的实现将标准库功能定义在全局空间里,声明在带.h后缀的头文件里,c++标准为了和C区别开,也为了正确使用命名空间,规定头文件不使用后缀.h。
因此,当使用<iostream.h>
时,相当于在c中调用库函数,使用的是全局命名空间,也就是早期的c++实现;当使用<iostream>
的时候,该头文件没有定义全局命名空间,必须使用namespacestd;这样才能正确使用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”
实际上就是告诉编译器,你类型是什么,在哪能找到。
常用的是using namespace std,就是说用C++的标准名字空间。
你也可以引用你自己的名字空间。比如说:
import "C:\\MyTest\\test.tlb"
using namespace CMyTest
就可以引用CMyTest内的各个类型名
看C++ prime
声明该文件使用C++标准库吧!
比如
#include <iostream>
using namespace std;
void main()
{
cout<< "hello!"<< endl;
}
如果不用using namespace std;这句,那么
std::cout << “hello!”<
// z.cpp
#include"x.h"
#include"y.h"
void z::f()
{
//声明一个文件x.h中类MyClass的实例x
MyNamespace1::MyClass x;
//声明一个文件x.h中类MyClass的实例x
MyNamespace2::MyClass y;
//调用文件x.h中的函数f
x.f();
//调用文件y.h中的函数f
y.f();
}
名字空间实质上是一个作用域。
通过上面的一个实例应该知道名字空间的作用了吧
尽量不要使用using namespace std;VC++2005使用有感
Posted on 2007-11-06 20:28 Samson小天 阅读(1163) 评论(6) 编辑 收藏 网摘 所属分类:C++/C++.net
今天用了VISUAL C++写了个小程序(VS2005),很简单很简单的,但是就是编译不通过
出现一个奇怪的问题:错误 1 error C2668: “max”: 对重载函数的调用不明确
最初代码如下
#include <iostream>
using namespace std;
template <typename T>
T max (T a,T b)
{
return ((a>b)?a:b);
}
void main()
{
double x,y;
cin>>x>>y;
cout<<"Max number is"<<(max(x,y))<<endl;
cin>>x;
}
我将这段代码放到VC++ 6.0下竟然通过了,程序运行也正常。这让我百思不得其解。后来终于弄明白了!
其实在std命名空间下还有一个MAX函数,而且实现的功能也是一样的……我昏。利用转到定义功能可以看到微软是怎么写MAX函数的。这里为了不被鄙视就不贴微软的代码了。
明白了为什么出现这个错误我们就改写代码如下:
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
template <typename T>
T max (T a,T b)
{
return ((a>b)?a:b);
}
int main()
{
double x,y;
cin>>x>>y;
cout<<"Max number is"<<(max(x,y))<<endl;
cin>>x;
}
这是我比较推荐的做法,因为C++ PRIMER, EFFECTIVE C++上都是用这种方式的,但是谭浩强的书上都是一句usingnamespace std;就搞定,我觉得蛮简洁的就一直用了,没想到带来那么多的问题,以前在友元函数上还碰到莫名的错误呢。
其实还有两个简单的解决方案,那就是把自己定义的函数改成其他的名字,或者直接用微软提供的函数。相信微软提供的效率绝对不会比我们写的低~
好了,就写到这了。希望大家养成良好的编程习惯,^-^
很多C++程序员还在使用而不是用更新的标准的库。
这两者都有什么不同呢?首先,5年前我们就开始反对把.h符号继续用在标准的头
文件中。继续使用过时的规则可不是个好的方法。从功能性的角度来讲,
<iostream>
包含了一系列模板化的I/O类,相反地
#include <conio.h>
#include <iostream.h>
namespace car // 名空间的定义
{
int model;
int length;
int width;
}
namespace plane
{
int model;
namespace size // 名空间的嵌套
{
int length;
int width;
}
}
namespace car // 添加名空间的成员
{
char * name;
}
namespace c=car; // 定义名空间的别名
int Time; // 外部变量属于全局名空间
void main()
{
car::length=3;
// 下面一句错误,故屏蔽掉
// width=2; // 对于非全局变量和当前有效临时变量应该指定名空间
plane::size::length=70;
cout<<"the length of plane is "<<plane::size::length<<"m."<<endl;
cout<<"the length of car is "<<car::length<<"m."<<endl;
// 使用名空间的别名
cout<<"the length of c is "<<c::length<<"m."<<endl;
int Time=1996; // 临时变量,应区别于全局变量
::Time=1997;
cout<<"Temp Time is "<<Time<<endl;
cout<<"Outer Time is "<<::Time<<endl;
// 使用关键字using
using namespace plane;
model=202;
size::length=93;
cout<<model<<endl;
cout<<size::length<<endl;
getch();
}
运行结果:
the length of plane is 70m.
the length of car is 3m.
the length of c is 3m.
Temp Time is 1996
Outer Time is 1997
说明:
• 从上面可以看出,名空间定义了一组变量和函数,它们具有相同的作用范围。对于不同的
名空间,可以定义相同的变量名或函数名,在使用的时候,只要在变量名或函数名前区分
开不同的名空间就可以了。
• 名空间可以被嵌套定义,使用时要逐级对成员用名空间限定符: :来引用。
• 系统默认有一个全局名空间,它包含了所有的外部变量。这个名空间没有名字,引用这个
名空间里的变量时要使用名空间限定符: :,前面没有名字。在不使用名空间的情况下,我
们知道,不可以在不同文件中定义相同名字的外部变量,这是因为它们属于同一个全局名
空间,名字不可以重复。
• 可以给名空间取一个别名。一般别名是一个比较短的名字,来简化编程。
• 在原有定义好的名空间的基础上,随时可以往里增加成员。
<<using>>
在前面的例程中可以看到,为了使用时的方便,又引入了关键字using。利用using声明可以在引用名空间成员时不必使用名空间限定符::。此外,关键字namespace和using的使用,对函数重载有一定的影响。
下面通过例程进行具体说明。
#include <conio.h>
#include <iostream.h>
namespace car // 名空间的定义
{
void ShowLength(double len) // 参数类型为d o u b l e
{
cout<<"in car namespace: "<<len<<endl;
}
}
namespace plane // 名空间的定义
{
void ShowLength(int len) // 参数类型为i n t
{
cout<<"in plane namespace: "<<len<<endl;
}
}
void main()
{
using namespace car;
ShowLength(3);
ShowLength(3.8);
using namespace plane;
ShowLength(93);
ShowLength(93.75);
getch();
}
运行结果:
in car namespace: 3
in car namespace: 3.8
in plane namespace: 93
in car namespace: 93.75
说明:
如果没有名空间的干扰,函数重载时选择规则将是非常简单。只要实参是double类型,则调用的是前面的函数;如果实参是int类型,则调用后面的函数。但是由于名空间的参与,就出现了上面的运行结果。所以在编程的时候一定要注意名空间对函数重载的影响。
应注意:调用函数时,如果实参和形参的数据类型实在没有办法完全匹配,可能会对实参进行适当的数据类型转换。比如,将char类型转换为int类型,或进一步将int类型转换为double类型。这种是将数据类型从简单往复杂转换,一般不会丢失信息。另外一种转换是反过来,将double类型转换为int类型,或进一步将int类型转换为char类型。这种是将数据类型从复杂往简单转换,可能会丢失部分信息。在调用函数的时候,不同的情况下,C++对上述两种转换的优先级是不同的。当引入了名空间后,则参与了上述优先级顺序的分配。
全局空间最大的问题在于它本身仅有一个。在大的软件项目中,经常会有不少人把他们定义的名字都放在这个单一的空间中,从而不可避免地导致名字冲突。例如,假设library1.h定义了一些常量,其中包括:
const double lib_version = 1.204;
类似的,library2.h也定义了:
const int lib_version = 3;
如果某个程序想同时包含library1.h和library2.h就会有问题。作为程序员,尽力使自己的程序库不给别人带来这些问题。例如,可预先想一些不大可能造成冲突的某种前缀,加在每个全局符号前。当然得承认,这样组合起来的标识符看起来不是那么令人舒服。
另一个比较好的方法是使用c++namespace。namespace本质上和使用前缀的方法一样,只不过避免了别人总是看到前缀而已。所以,不要这么做:
const double sdmbook_version =2.0; // 在这个程序库中, 每个符号以"sdm"开头
// class sdmhandle { ... };
sdmhandle&sdmgethandle(); // 为什么函数要这样声明?
而要这么做:
namespace sdm {
const double book_version = 2.0;
class handle { ... };
handle& gethandle();
}
用户于是可以通过三种方法来访问这一名字空间里的符号:将名字空间中的所有符号全部引入到某一用户空间;将部分符号引入到某一用户空间;或通过修饰符显式地一次性使用某个符号:
void f1()
{
using namespacesdm; // 使得sdm中的所有符号不用加修饰符就可以使用
cout <<book_version; // 解释为sdm::book_version
...
handle h =gethandle(); // handle解释为sdm::handle,
// gethandle解释为sdm::gethandle
...
}
void f2()
{
usingsdm::book_version; // 使得仅book_version不用加修饰符就可以使用
cout <<book_version; // 解释为sdm::book_version
...
handle h =gethandle(); // 错误! handle和gethandle
// 都没有引入到本空间
...
}
void f3()
{
cout <<sdm::book_version; // 使得book_version在本语句有效
...
double d =book_version; // 错误! book_version不在本空间
handle h =gethandle(); // 错误! handle和gethandle都没有引入到本空间
...
}
有些名字空间没有名字。这种没命名的名字空间一般用于限制名字空间内部元素的可见性。
名字空间带来的最大的好处之一在于:潜在的二义不会造成错误。所以,从多个不同的名字空间引入同一个符号名不会造成冲突(假如确实真的从不使用这个符号的话)。例如,除了名字空间sdm外,假如还要用到下面这个名字空间:
namespace acmewindowsystem {
...
typedef int handle;
...
}
只要不引用符号handle,使用sdm和acmewindowsystem时就不会有冲突。假如真的要引用,可以明确地指明是哪个名字空间的handle:
void f()
{
using namespacesdm; // 引入sdm里的所有符号
using namespaceacmewindowsystem; // 引入acme里的所有符号
... // 自由地引用sdm和acme里除handle之外的其它符号
handleh; // 错误! 哪个handle?
sdm::handleh1; // 正确, 没有二义
acmewindowsystem::handleh2; // 也没有二义
...
}
假如用常规的基于头文件的方法来做,只是简单地包含sdm.h和acme.h,这样的话,由于handle有多个定义,编译将不能通过。
名字空间的概念加入到c++标准的时间相对较晚,所以有些人会认为它不太重要,可有可无。但这种想法是错误的,因为c++标准库里几乎所有的东西都存在于名字空间std之中。它有一种直接的影响方式:c++提供了那些没有扩展名的头文件,如<iostream>,<string>
等。
由于名字空间的概念引入的时间相对较晚,有些编译器可能不支持。就算是这样,那也没理由污染全局名字空间,因为可以用struct来近似实现namespace。可以这样做:先创建一个结构用以保存全局符号名,然后将这些全局符号名作为静态成员放入结构中:
// 用于模拟名字空间的一个结构的定义
struct sdm {
static const double book_version;
class handle { ... };
static handle&gethandle();
};
const double sdm::book_version =2.0; // 静态成员的定义
现在,如果有人想访问这些全局符号名,只用简单地在它们前面加上结构名作为前缀:
void f()
{
cout <<sdm::book_version;
...
sdm::handle h = sdm::gethandle();
...
}
如果全局范围内实际上没有名字冲突,用户就会觉得加修饰符麻烦而多余。幸运的是,还是有办法来让用户选择使用它们或忽略它们。
对于类型名,可以用类型定义(typedef)来显式地去掉空间引用。例如,假设结构s(模拟的名字空间)内有个类型名t,可以这样用typedef来使得t成为s::t的同义词:
typedef sdm::handle handle;
对于结构中的每个(静态)对象x,可以提供一个(全局)引用x,并初始化为s::x:
const double& book_version = sdm::book_version;
处理函数的方法和处理对象一样,但要注意,即使定义函数的引用是合法的,但代码的维护者会更喜欢使用函数指针:
sdm::handle& (* const gethandle)()= // gethandle是指向sdm::gethandle
sdm::gethandle; // 的const 指针
注意gethandle是一个常指针。因为当然不想让用户将它指向别的什么东西,而不是sdm::gethandle。
如果真想知道怎么定义一个函数的引用,看看下面:
sdm::handle& (&gethandle)()= // gethandle是指向
sdm::gethandle; // sdm::gethandle的引用
除了初始化的方式外,函数的引用和函数的常指针在行为上完全相同,只是函数指针更易于理解。
有了上面的类型定义和引用,那些不会遭遇全局名字冲突的用户就会使用没有修饰符的类型和对象名;相反,那些有全局名字冲突的用户就会忽略类型和引用的定义,代之以带修饰符的符号名。还要注意的是,不是所有用户都想使用这种简写名,所以要把类型定义和引用放在一个单独的头文件中,不要把它和(模拟namespace的)结构的定义混在一起。
struct是namespace的很好的近似,但实际上还是相差很远。它在很多方面很欠缺,其中很明显的一点是对运算符的处理。如果运算符被定义为结构的静态成员,它就只能通过函数调用来使用,而不能象常规的运算符所设计的那样,可以通过自然的语法来使用:
// 定义一个模拟名字空间的结构,结构内部包含widgets的类型
// 和函数。widgets对象支持operator+进行加法运算
struct widgets {
class widget { ... };
static const widgetoperator+(const widget& lhs,constwidget& rhs);
...
};
// 为上面所述的widge和operator+ 建立全局(无修饰符的)名称
typedef widgets::widget widget;
const widget (* const operator+)(constwidget&, // 错误!
constwidget&); // operator+不能是指针名
widget w1, w2, sum;
sum = w1 +w2; // 错误! 本空间没有声明
// 参数为widgets 的operator+
sum = widgets::operator+(w1,w2); // 合法, 但不是"自然"的语法
正因为这些限制,所以一旦编译器支持,就要尽早使用真正的名字空间。