第六章 名称空间本章主要内容
1,名称冲突
2,内部连接与外部连接
3,名称空间的定义和使用
22:20:01
§ 1 名称冲突
与标识符的作用范围相关:在同一个作用域内,不能有两个完全一样的标识符
工程中包含多个文件时,由于有些标识符的作用范围是可以扩展的,因此会造成标识符的名称冲突
团队开发中,不同模块由不同程序员完成,各人都有自己给标识符命名的习惯,不同模块中的标识符之间可能存在名字上的冲突例:变量名的冲突
//main.cpp
extern int nTestVar;
void main()
{
nTestVar = 0;
}
//defvar.cpp
int nTestVar;
//another.cpp
int nTestVar;
连接时无法确定是哪一个文件中定义的变量 nTestVar
同一工程中的文件
22:20:01
例:函数名的冲突
void funcTest();
void main()
{
funcTest();
}
//defvar.cpp
void funcTest()
{ }
//another.cpp
void funcTest()
{ }
同一工程中的文件连接时无法确定是哪一个文件中定义的函数 funcTest();
待开发的系统越大,名字的冲突越严重
一旦发生名字的冲突,会造成对源代码的大量修改,增大了开发工作量,且在修改过程中可能引入新的错误
在团队开发中,这种情况尤其严重函数重复定义
22:20:01
§ 2 内部连接与外部连接
源程序必须翻译成二进制的机器代码,才能被执行
C++的翻译采用 编译 +连接 的方式,即工程中所有源程序文件 (.CPP文件 )经过编译程序的处理,得到相应的目标程序文件 (.obj文件 ),连接程序再把这些目标程序文件与系统提供的库函数,类等进行连接 (组装 ),得到可执行的程序
根据标识符的属性不同,连接方式有两种
.内部连接,外部连接
连接程序的任务之一是建立标识符与内存地址之间的对应关系,即 确定标识符代表的变量或函数等在内存中的位置
22:20:01
外部连接
作用域为文件范围的标识符,连接程序处理工程中的任何一个文件时,该标识符对于连接程序都是可见的,称这种标识符 具有外部连接属性
全局变量和全局函数都具有外部连接属性
具有外部连接属性的标识符可能会造成名称冲突
内部连接
静态函数,全局静态变量对应的标识符,只在定义该标识符的文件中起作用,连接程序处理工程中其它文件时,该标识符对连接程序是不可见的,称这种标识符 具有内部连接属性
具有内部连接属性的标识符不会与其它文件,模块中标识符的名称发生冲突局部变量对应的标识符只在定义它的函数中起作用,不会与其它标识符发生冲突,所以一般不讨论其连接属性
22:20:01
例:外部连接与内部连接
//fTest1.cpp
int nConflict;
static int nNoConflict;
void funcConflict(){ }
static void funcNoConflict(){ }
//fTest2.cpp
int nConflict;
static int nNoConflict;
void funcConflict(){ }
static void funcNoConflict(){ }
//main.cpp
extern int nConflict;
extern int nNoConflict;
void funcConflict();
void funcNoConflict();
void main(){
nConflict = 1;
nNoConflict = 2; //错
funcConflict();
funcNoConflict(); /*错 */ }
冲突:外部连接不冲突:内部连接
22:20:01
§ 3 名称空间 (namespace)
名称空间在 C++中是为了对全局标识符的名称进行灵活,
高效的管理和使用而引入的概念
把一个全局的名称空间分成多个小空间,每个全局标识符对应的名字都属于其中某个名称空间,不同名称空间中的标识符可以同名,但因所在空间不同 (作用范围不同 )而不会发生冲突,从而解决了名称冲突的问题
22:20:01
1.名称空间的创建:与创建一个类相似例,namespace MyLib{
class X{ };
void funcTest(){ }
}
说明:
定义 namespace的 }后不加分号,;”
名称空间内部可以包含类,普通函数,全局变量等全局标识符的定义或声明,也可以包含名称空间的嵌套定义
namespace只能在全局范围内定义
名称空间是开放的,一个 namespace可以在多个头文件中用同一个标识符定义,该名称空间将是这多个定义的并集
名称空间不是数据类型,不能定义名称空间的变量
22:20:01
//classX.cpp
#include,classX.h”
MyLib::classX::classX(int xx)
{ x=xx; }
void MyLib::classX::showX()
{ cout << x << endl; }
using namespace MyLib;
classX::classX(int xx)
= xx; }
d classX::showX()
endl;}
例:名称空间的定义
//classX.h
namespace MyLib{
class classX{
int x;
public:
classX(int xx);
void showX();
};
}
22:20:01
//classY.cpp
#include,classY.h”
MyLib::classY::classY(int yy)
{ y=yy; }
void MyLib::classY::showY()
{ cout << y << endl;}
using namespace MyLib;
classY::classY(int yy)
= yy; }
d classY::showY()
endl; }
//classY.h
namespace MyLib{
class classY{
int y;
public:
classY(int yy);
void showY();
};
}
22:20:01
2.名称空间的使用:三种方法
作用范围解析例:类 classX和 classY成员函数的实现时,使用了作用范围解析的方式
//classY.cpp
#include,classY.h”
MyLib::classY::classY(int yy)
{ y = yy; }
void MyLib::classY::showY()
{ cout << y << endl;}
//classX.cpp
#include,classX.h”
MyLib::classX::classX(int xx)
{ x = xx; }
void MyLib::classX::showX()
{ cout << x << endl; }
22:20:01
//main.cpp
#include "classX.h"
using namespace MyLib;
void main(){
classX xObj(1);
xObj.showX();
}
使用 using namespace命令,将一个名称空间中的标识符引入当前范围内:即打开名称空间例,using namespace的使用
#include <iostream>
class Complex{ …
friend std::ostream& operator<<(std::ostream& os,
Complex& c);
friend std::istream& operator>>(std::istream& is,
Complex& c);
};
若在头文件 (.h) 中使用 using namespace命令,则所有包含该头文件的,cpp文件中也打开了该名称空间,故一般不在头文件中使用该命令
在头文件中需要使用名称空间中的标识符时,应包含相应的头文件,然后使用名称空间名,:标识符的形式 (即上一种形式 )
22:20:01
使用声明只将被声明的一个名字引入到当前范围内,该名称空间中其它名字并没有被引入例:使用声明
#include <iostream>
namespace DemoNameSpace
{
void f(){
std::cout << "This is in DemoNameSpace::f()“
<< std::endl;
}
void g(){
std::cout << "This is in DemoNameSpace::g()“
<< std::endl;
}
}
22:20:01
例:使用声明 (续 )
#include "namespace.h"
void main()
{
using DemoNameSpace::f;
f(); //正确
g(); // 错
DemoNameSpace::g(); //正确
}
说明:
全局函数 f()与 g()都定义在名称空间 DemoNameSpace中,在
main()函数中 对该名称空间中的标识符 f 进行了声明,但 没有对 g 进行声明,因此 名称 f 在 main()中 直接可见,而 名称
g 在 main()中 不直接可见,必须使用 作用范围解析 的方法
22:20:01
1,名称冲突
2,内部连接与外部连接
3,名称空间的定义和使用
22:20:01
§ 1 名称冲突
与标识符的作用范围相关:在同一个作用域内,不能有两个完全一样的标识符
工程中包含多个文件时,由于有些标识符的作用范围是可以扩展的,因此会造成标识符的名称冲突
团队开发中,不同模块由不同程序员完成,各人都有自己给标识符命名的习惯,不同模块中的标识符之间可能存在名字上的冲突例:变量名的冲突
//main.cpp
extern int nTestVar;
void main()
{
nTestVar = 0;
}
//defvar.cpp
int nTestVar;
//another.cpp
int nTestVar;
连接时无法确定是哪一个文件中定义的变量 nTestVar
同一工程中的文件
22:20:01
例:函数名的冲突
void funcTest();
void main()
{
funcTest();
}
//defvar.cpp
void funcTest()
{ }
//another.cpp
void funcTest()
{ }
同一工程中的文件连接时无法确定是哪一个文件中定义的函数 funcTest();
待开发的系统越大,名字的冲突越严重
一旦发生名字的冲突,会造成对源代码的大量修改,增大了开发工作量,且在修改过程中可能引入新的错误
在团队开发中,这种情况尤其严重函数重复定义
22:20:01
§ 2 内部连接与外部连接
源程序必须翻译成二进制的机器代码,才能被执行
C++的翻译采用 编译 +连接 的方式,即工程中所有源程序文件 (.CPP文件 )经过编译程序的处理,得到相应的目标程序文件 (.obj文件 ),连接程序再把这些目标程序文件与系统提供的库函数,类等进行连接 (组装 ),得到可执行的程序
根据标识符的属性不同,连接方式有两种
.内部连接,外部连接
连接程序的任务之一是建立标识符与内存地址之间的对应关系,即 确定标识符代表的变量或函数等在内存中的位置
22:20:01
外部连接
作用域为文件范围的标识符,连接程序处理工程中的任何一个文件时,该标识符对于连接程序都是可见的,称这种标识符 具有外部连接属性
全局变量和全局函数都具有外部连接属性
具有外部连接属性的标识符可能会造成名称冲突
内部连接
静态函数,全局静态变量对应的标识符,只在定义该标识符的文件中起作用,连接程序处理工程中其它文件时,该标识符对连接程序是不可见的,称这种标识符 具有内部连接属性
具有内部连接属性的标识符不会与其它文件,模块中标识符的名称发生冲突局部变量对应的标识符只在定义它的函数中起作用,不会与其它标识符发生冲突,所以一般不讨论其连接属性
22:20:01
例:外部连接与内部连接
//fTest1.cpp
int nConflict;
static int nNoConflict;
void funcConflict(){ }
static void funcNoConflict(){ }
//fTest2.cpp
int nConflict;
static int nNoConflict;
void funcConflict(){ }
static void funcNoConflict(){ }
//main.cpp
extern int nConflict;
extern int nNoConflict;
void funcConflict();
void funcNoConflict();
void main(){
nConflict = 1;
nNoConflict = 2; //错
funcConflict();
funcNoConflict(); /*错 */ }
冲突:外部连接不冲突:内部连接
22:20:01
§ 3 名称空间 (namespace)
名称空间在 C++中是为了对全局标识符的名称进行灵活,
高效的管理和使用而引入的概念
把一个全局的名称空间分成多个小空间,每个全局标识符对应的名字都属于其中某个名称空间,不同名称空间中的标识符可以同名,但因所在空间不同 (作用范围不同 )而不会发生冲突,从而解决了名称冲突的问题
22:20:01
1.名称空间的创建:与创建一个类相似例,namespace MyLib{
class X{ };
void funcTest(){ }
}
说明:
定义 namespace的 }后不加分号,;”
名称空间内部可以包含类,普通函数,全局变量等全局标识符的定义或声明,也可以包含名称空间的嵌套定义
namespace只能在全局范围内定义
名称空间是开放的,一个 namespace可以在多个头文件中用同一个标识符定义,该名称空间将是这多个定义的并集
名称空间不是数据类型,不能定义名称空间的变量
22:20:01
//classX.cpp
#include,classX.h”
MyLib::classX::classX(int xx)
{ x=xx; }
void MyLib::classX::showX()
{ cout << x << endl; }
using namespace MyLib;
classX::classX(int xx)
= xx; }
d classX::showX()
endl;}
例:名称空间的定义
//classX.h
namespace MyLib{
class classX{
int x;
public:
classX(int xx);
void showX();
};
}
22:20:01
//classY.cpp
#include,classY.h”
MyLib::classY::classY(int yy)
{ y=yy; }
void MyLib::classY::showY()
{ cout << y << endl;}
using namespace MyLib;
classY::classY(int yy)
= yy; }
d classY::showY()
endl; }
//classY.h
namespace MyLib{
class classY{
int y;
public:
classY(int yy);
void showY();
};
}
22:20:01
2.名称空间的使用:三种方法
作用范围解析例:类 classX和 classY成员函数的实现时,使用了作用范围解析的方式
//classY.cpp
#include,classY.h”
MyLib::classY::classY(int yy)
{ y = yy; }
void MyLib::classY::showY()
{ cout << y << endl;}
//classX.cpp
#include,classX.h”
MyLib::classX::classX(int xx)
{ x = xx; }
void MyLib::classX::showX()
{ cout << x << endl; }
22:20:01
//main.cpp
#include "classX.h"
using namespace MyLib;
void main(){
classX xObj(1);
xObj.showX();
}
使用 using namespace命令,将一个名称空间中的标识符引入当前范围内:即打开名称空间例,using namespace的使用
#include <iostream>
class Complex{ …
friend std::ostream& operator<<(std::ostream& os,
Complex& c);
friend std::istream& operator>>(std::istream& is,
Complex& c);
};
若在头文件 (.h) 中使用 using namespace命令,则所有包含该头文件的,cpp文件中也打开了该名称空间,故一般不在头文件中使用该命令
在头文件中需要使用名称空间中的标识符时,应包含相应的头文件,然后使用名称空间名,:标识符的形式 (即上一种形式 )
22:20:01
使用声明只将被声明的一个名字引入到当前范围内,该名称空间中其它名字并没有被引入例:使用声明
#include <iostream>
namespace DemoNameSpace
{
void f(){
std::cout << "This is in DemoNameSpace::f()“
<< std::endl;
}
void g(){
std::cout << "This is in DemoNameSpace::g()“
<< std::endl;
}
}
22:20:01
例:使用声明 (续 )
#include "namespace.h"
void main()
{
using DemoNameSpace::f;
f(); //正确
g(); // 错
DemoNameSpace::g(); //正确
}
说明:
全局函数 f()与 g()都定义在名称空间 DemoNameSpace中,在
main()函数中 对该名称空间中的标识符 f 进行了声明,但 没有对 g 进行声明,因此 名称 f 在 main()中 直接可见,而 名称
g 在 main()中 不直接可见,必须使用 作用范围解析 的方法
22:20:01