目录
- 将原来的程序分为三部分:头文件、主函数和子函数
- 静态持续性、外部链接性
- 静态持续性、内部链接性
- 静态存储持续性、无链接性(涉及到一个静态变量在字符串输入的应用)
- 存储方案和动态分配
- 定位new运算符
- 名称空间特性
- 名称空间示例
- 名称空间的使用规范
将原来的程序分为三部分:头文件、主函数和子函数
/*
01)原来的源程序可以分为三部分:
A 头文件:包含结构声明和使用这写结构的函数的原型
B 源代码文件:包含与结构有关的函数的代码(主函数)
C 源代码文件:包含调用与结构相关的代码(子函数)
02)头文件中常包含的内容:
函数原型(函数声明)、使用#define或const定义的符号常量
结构声明、类声明、模板声明、内联函数
03)在包含头文件时,我们使用"coordin.h",而不是<coordin.h>。
如果头文件包含在尖括号内,则c++编译器将在存储标准头文件的主机系统的文件系统中查找;
如果头文件包含在双引号中,则编译器将首先查找当前的工作目录或源代码目录;如果没有在
那里找到头文件,则将在标准位置查找,因此在包含自己的头文件时候,应该使用双引号而不是尖括号
*/
存储类型、作用域和链接性
01)自动存储:在函数内部定义的常规变量使用自动存储空间,被称为自动变量,这意味着它们在所属的函数被调用时
自动生成,在该函数结束时消亡。自动变量通常存储在栈中,这意味着其中变量依次加入到栈中,而在离开栈时,
将按相反的顺序释放这些变量,这被称为"后进先出(LIFO)",因此在程序执行的过程中,栈将不断的减小或增大。
02)静态存储:是一种在整个程序执行期间都存在的存储方式,使变量称为静态的方式有两种:一种是在函数外满定义
变量,另一种是在声明变量时使用关键字stctic。
03)动态存储:new是delete提供的存储方式。它们管理一个内存池,这在c++这被称为自由存储空间(free store)或
堆(heap),该内存池同用于自动变量和自动变量的内存是分开的。
04)作用域: 描述了名称在文件(翻译单元)的多大范围内课件。例如在函数定义的变量可在该函数中使用,但在
其他函数则不可以;而在函数外面定义的变量则可以在所有函数中使用。
05)链接性: 描述了名称如何在不同单元间共享。链接性为内部的名称只能由一个文件中的函数共享。
自动变量没有连接性,因为它们不能共享。
自动存储持续性
01)在默认情况下,在函数中声明的函数参数和变量的存储持续性为自动,作用域为局部,没有链接性。也就是说
如果mian()函数中声明了一个texas的变量,在子函数oil()中也声明了一个texas变量,则创建两个独立的变量,
只有在定义它们的函数中才可以使用它们。对oil()函数中的texas变量执行的任何操作不会影响main()函数中的
texas变量,反之亦然。例如如下代码块
int main()
{
int teledeli = 5;
//定义代码块
{
int teledeli = 23;
teledeli = teledeli+1;
cout<<"Hello"<<teledeli<<endl; //打印的是24
}
teledeli = teledeli+1;
cout<<"Hello"<<teledeli<<endl; //打印的是6
}
02)寄存器变量: 关键字register最初是由c语言提出的,它建议编译器使用CPU寄存器来存储自动变量,这旨在
提高访问变量的速度。
register int cout_fast; //声明一个寄存器变量
静态持续变量&static的两种用法
01)c++为静态存储持续性变量提供了三种链接性:外部链接性(可在其他文件中访问,用static声明)、
内部链接性(只能在当前文件中访问,用static声明)和无连接型(只能在当前函数代码块或子函数
中访问,用static声明)。这三种链接性的变量在整个程序执行期间都存在,
与自动变量相比,他们的寿命更长。由于静态变量在程序运行期间是不变的,故程序不需要使用特殊的装
置(如栈)来存储他们,编译器只需要给他们分配固定的内存块即可。另外,如果没有显式的初始化静态变量,编
译器将把他们设置为0。在默认情况下,静态数组和结构将每个元素或成员的所有位设置为0.
现举例说明如何创建三种链接类型的变量:
...
int global = 1000; //global具有外部链接性,在程序的其他文件只也可以使用
//如何在其他cpp文件中使用另外一个cpp文件中的外部变量(即上面的变量global)?使用关键字extern,接下来将会介绍
//下面一句中的static表示的是内部链接性,变量是静态持续性
static int one_file = 50; //one_file具有内部链接性,只能在包含上述代码的文件中使用它
int main()
{
....
}
void fun1(int n)
{
//此时static表示的是存储持续性
static int count = 0; //count没有链接性,作用域为局部,这意味着只能在fun1只使用它
int llama = 0; //与llama不同的是,即使fun1()函数没有被执行,count也留在内存中
}
02)static的两种用法:
A 用于局部声明(函数内声明变量),以指出变量时无连接型的静态变量时,static标示的是存储持续性;
B 用于代码块外的声明时,static表示的是内部链接性,而变量已经是静态持续性了
总结如下表:
静态持续性、外部链接性
01)变量的定义方法(两种):
A 定义声明(简称定义):它给变量分配存储空间
B 引用声明(简称声明):它不给变量分配存储UKon关键,因为它引用已有的变量,
引用变量使用关键字extern,且不能给变量初始化,否则声明变为定义,导致分配存储空间
double up; //定义
extern int blem; //声明,不能初始化
extern char gr = 'A'; //定义,虽然使用了extern,但是给变量初始化了
02)一方面,在每个使用外部变量(在函数外定义的变量)都必须声明它,
另一方面:变量只能定义一次(单定义规则)
03)如果在多个cpp文件中使用main.cpp或其他cpp文件中的外部定义,只需在一个cpp文件中包含该变量
的定义(单定义规则),但在其他使用改变量的其他cpp文件中,都必须使用关键字extern声明它,如:
//file01.cpp
extern int cats = 20; //extern可以省略,加上也是可以的
int dogs = 22;
int fleas;
//file02.cpp
extern int cats; //使用extern之后,在file02.cpp之后就可以使用file01.cpp中的cats变量
extern int dogs; //使用extern之后,在file02.cpp之后就可以使用file01.cpp中的dogs变量
//file03.cpp
extern int cats; //使用extern之后,在file03.cpp之后就可以使用file01.cpp中的cats变量
extern int dogs; //使用extern之后,在file03.cpp之后就可以使用file01.cpp中的dogs变量
extern int fleas; //使用extern之后,在file03.cpp之后就可以使用file01.cpp中的fleas变量
1 //external.cpp
2 //compile with support.cpp
3
4 #include <iostream>
5
6 using namespace std;
7
8 double warming = 0.3; //定义外部变量(全局变量),在support.cpp中也可以使用的
9
10 void update(double dt); //声明一个函数,该函数在support.cpp中定义
11 void local(); //声明一个函数,该函数在support.cpp中定义
12
13 int main()
14 {
15 cout << "Global waiming is " << warming << endl;
16 update(0.1); //调用support.cpp中定义的函数
17 cout << "Global waiming is " << warming << endl;
18 local(); //调用support.cpp中定义的函数
19 cout << "Global waiming is " << warming << endl;
20
21 system("pause");
22 return 0;
23 }
1 //surpport.cpp
2 //compile with external.cpp
3
4 #include <iostream>
5
6 void update(double dt); //声明一个函数
7 void local(); //声明一个函数
8
9 using std::cout;
10 using std::endl;
11
12 extern double warming; //声明引用变量,即声明在外部文件(external.cpp)中定义的变量
13
14 void update(double dt)
15 {
16 extern double warming; //声明引用变量,即声明在外部文件(external.cpp)中定义的变量,由于在函数外已经使用extern声明了warming,所以此处不写这一句也是可以的
17 warming = warming + dt; //对在external.cpp中的外部变量进行修改,此处的修改会在external.cpp中保存
18 cout << "Updating global warming to " << warming << endl;
19 }
20
21 void local()
22 {
23 double warming = 0.8; //声明一个局部变量
24 cout << "Local warming is " << warming << endl;
25 cout << "But global warming is " << ::warming << endl; //c++提供了作用域解析运算符::,放在变量前该运算符表示使用全局版本
26
27 }
程序说明:
01) c++提供了作用域解析运算符::,放在变量前该运算符表示使用全局版本
02) 另外在external.cpp中定义的外部(全局)变量,如果要在support.cpp中使用,但是必须是在support.cpp中函数外部用extern声明一下
03)如果在外部定义了一个warming变量,在一个子函数内也定义了一个相同名字的变量warming,那么执行到该子函数的时候,子函数内的
warming会覆盖掉外部定义的warming变量
在external.cpp文件中执行的结果:
静态持续性、内部链接性
01)在函数外定义的变量具有外部链接性,可以在不同程序文件之间共享数据
在函数内定义或者是用关键字static定义的变量具有内部连接性,只能在本cpp文件内共享用该变量
名称空间提供了另外一种共享数据的方法
02)下面介绍一种错误的做法:
//file01.cpp
int errors = 20;
//file02.cpp
int errors = 5;//这里将会报错,因为同时在两个cpp文件中同时定义了一个名字相同的变量,是不合法的
03)改进(使用static关键字):
//file01.cpp
int errors = 20; //外部(全局)变量errors具有外部链接性
void main()
{
....
}
//file02.cpp
static int errors = 5; //这样是允许的,因为这个errors变量具有内部连接性
void support()
{
....
}
04)在多程序文件中,可以在一个文件(且只能在一个文件)中定义一个外部变量。使用该变量的其他文件使用
关键字extern声明该变量。
05)可以将作用域为单独某一个cpp文件的变量设置为静态的(使用static声明),就不必担心其名称与外部变量
发生冲突。即防止出现02)类似的错误。
//该程序演示了c++佮处理链接性为外部和内部的变量
1 //twofile1.cpp 该文件件包含了main()
2 //compile with twofile2.cpp
3 //该程序演示了c++佮处理链接性为外部和内部的变量
4 #include <iostream>
5
6 int tom = 3; //定义外部(全局)变量,链接性为外部
7 int dick = 30; //定义外部(全局)变量,链接性为外部
8 static int harry = 300; //定义静态外部变量,链接性为内部,即harry的作用域为twofile1.cpp,不能作用于外部cpp文件
9
10 void remote_access(); //在twofile2.cpp中定义的函数
11
12 int main()
13 {
14 using namespace std;
15
16 cout << "外部变量tom的地址:" << &tom << " tom的值为:" << tom << endl;
17 cout << "外部变量dick的地址:" << &dick << " dick的值为:" << dick << endl;
18 cout << "静态外部变量harry的地址:" << &harry << " harry的值为:" << harry << endl;
19 remote_access(); //调用在twofile2.cpp中定义的函数
20
21 system("pause");
22 return 0;
23 }
1 //twofile2.cpp
2 //compile with twofile1.cpp
3
4 #include <iostream>
5
6 extern int tom; //使用twofile1.cpp中的外部变量tom
7 static int dick = 10; //使用自己cpp文件中定义的静态全局变量dick
8 int harry = 200; //不会与twofile1.cpp中定义的静态外部变量harry冲突,因此此处的harry的作用域为全部cpp文件
9
10 void remote_access()
11 {
12 using namespace std;
13
14 cout << "在子函数remote_access()中:" << endl;
15 cout << "外部变量tom的地址:" << &tom << " tom的值为:" << tom << endl;
16 cout << "twofile2.cpp中静态外部变量harry的地址:" << &harry << " harry的值为:" << harry << endl;
17 }
执行结果:
静态存储持续性、无链接性(涉及到一个应用,要看一下的)
01)无链接性的局部变量:将static限定符用于在代码块中定义的变量。在代码块使用static时,将导致局部
变量的持续性为静态的。这意味着该变量只在该代码块内可用,但它在该代码块不处于活动状态时仍然存
在。因此在函数调用之前,静态局部变量的值将保持不变。(静态局部变量适用于再生---可以将用静态局
部变量存储银行密码,传递到下一个地方去)。
02)如果初始化了静态局部变量。则程序只在启动时进行一次初始化,以后再调用该函数时,将不会像自动变量
那样再次被初始化。
1 #include <iostream>
2
3 const int ArSize = 10; //定义一个值不可被改变的外部(全部)变量,加了const之后,ArSize的作用域为该cpp文件,相当于加了static
4
5 void strcount(const char* str); //声明一个子函数
6
7 int main()
8 {
9 using namespace std;
10
11 char input[ArSize]; //定义一个c风格字符串数组,最多存储9个字符
12 char next; //定义一个字符变量,作用域为main()
13
14 cout << "请输入一行字符:";
15 cin.get(input, ArSize); //输入到字符串数组input中,最多可输入ArSize-1个字符,有一个位置要为空字符'\0'保留
16
17 while (cin) //如果输入的字符数小于ArSize,那么最后的回车键会被保留在输入流中。
18 { //如果输入流中有字符,那么cin的返回值一直是true(因为没有接收的变量)
19 cin.get(next); //如果输入的字符多于9个,那么输入到next中去
20 while (next != '\n')
21 cin.get(next); //一直将多输入的字符接收完
22 strcount(input); //子函数调用
23 cout << endl;
24 cout << "请输入一行字符:";
25 cin.get(input, ArSize);
26 }
27 cout << "Bye" << endl;
28
29 system("pause");
30 return 0;
31 }
32 void strcount(const char* str)
33 {
34 using namespace std;
35
36 static int total = 0; //只会初始化一次,以后被修改的值会被保留下来
37 int count = 0; //每次调用该函数的时候count都会被初始化一次
38
39 cout << "\"" << str << "\" contains "; //其中\"表示显示",\表示消除歧义的意思
40 while (*str != '\0')
41 {
42 count++;
43 str++;
44 }
45 total += count; //由于total是静态局部变量,所以total每次的值都会保存下来
46 cout << count << " characters" << endl;
47 cout << total << " characters total" << endl;
48 }
函数说明:
01)使用关键字static,在子函数中定义了一个静态局部变量total,total只会在子函数中被初始化一次,以后total
的值就会被保存下来,在函数开始的时候不会被初始化。*****
02)cin返回值相关,多输入的字符的处理方法等
03)定义的全局变量,加上const关键字之后,其作用域就是该cpp文件,而不是整个文件夹中的cpp文件了
执行结果:
存储方案和动态分配
01)通常,编译器使用三块独立的内存:一块用于静态变量(可能再细分)、一块用于自动变量、一块用于动态存储
02)动态内存由运算符new和delete控制,而不是由作用域和链接性控制
03)虽然存储方案不适用于动态存储,但适用于用来跟踪动态内存的额自动和静态指针变量。例如在一个函数中包含
语句: float* p_fees = new float [20];
由new分配的80个字节(假设float为4个字节)的内存将一直保留在内存中,知道使用delete运算符将其释放。
但当包含该声明的语句块执行完毕时,p_fees指针将消失,如果希望在另一个函数能够使用这80个字节的内容,
必须将其地址传递或返回给该函数。
另一方面,如果将p_fees的链接性声明为外部的,则文件中位于该声明之后的所有函数都可以使用它。另外,
通过在另一个cpp文件中使用下述声明,便可以使用该指针:
extern float* p_fees;
04)对单值变量使用new运算符初始化:使用小括号或者是大括号都可以
int* pi = new int (6); //表示*pi=6
double* pd = new double(99.99); //表示*pd = 99.99
int* pin = {6}; //表示*pin = 6
double* pd = new double{99.99}; //表示*pd = 99.99
05)对常规结构或数组使用new运算符进行初始化:需要使用大括号的列表初始化
struct where {double x; double y; double z}; //声明一个结构where
where* one = new where {2.5,5.3,7.2}; //新建一个结构one,使one指向where结构
int* ar = new int[4] {2,4,6,7}; //新建一个指针ar,指向一个包含四个元素的数组
06)运算符new和new[]运算符分别调用如下函数:
void * operator new(std::size_t); //used by new
void * operator new[](std::size_t); //used by new[],其中std::size_t是一个typedef
这些函数被成为分配函数,它们位于全局名称空间中
对于int* pi = new int;将被转换为int* pi = new(sizeof(int));
int* pa = new int [40];将被转换为int* pa = new(40*sizeof(int));
07)运算符delete和delete[]调用如下函数:
void operator delete(void *); //used by delete
void operator delete[](void *); //used by delete[]
这些函数被称为释放函数
定位new运算符
01)通常,new运算符负责在堆(heap)中找到一个足以能够满足要求的内存块。
new运算符还有另一种变体,被称为定位new运算符,它能够让程序员指定要使用的地方。程序员使用这种特性
来设置其内存管理规程、处理需要通过特定地址进行访问的硬件或在特定位置创建对象。
02)要使用定位new特性,首先要包含头文件new,它提高了这种版本的new运算符的原型(函数声明);然后将new运算符
用于提供了所需地址的函数,变量后面可以有大括号,也可以没有;
1 #include <iostream>
2 #include <new> //for new定位符
3
4 const int BUF = 512;
5 const int N = 5;
6 char buffer[BUF]; //定义一个字符串数组buffe,没有进行初始化.c风格字符串的大小用sizeof()函数,string类用size()
7
8 int main()
9 {
10 using namespace std;
11
12 double* pd1;
13 double* pd2;
14 pd1 = new double[N]; //pd1是一个指针(地址),new double[N]返回一个可以存储N个double型数据的地址
15 pd2 = new (buffer) double[N]; //pd2是一个指针(地址),new double[N]返回一个可以存储N个double型数据的地址
16 //而这个地址是buffer的地址,即pd2和buffer的地址是一样的,
17 //虽然buffer内数据类型为char,但是pd2指向的空间内存数据为double型的
18 cout << "Now calling new and new placement for first time" << endl;
19 cout << "使用new给pd1分配内存,指针pd1(一个地址)为:" << pd1 << endl;
20 cout << "使用定位符new将buffer的地址赋给pd2" << endl;
21 cout << "使用new定位符给pd2分配内存,指针pd2(一个地址)为:" << pd2 << endl;
22 cout << "buffer的地址为:" << (void *)buffer << endl; //由于buffer是一个字符串数组的名字,且数组的名字
23 //就是数组内第一个元素的地址,如果直接输出buffer的话,cout不会输出地址,而是输出buffer内的字符串
24 //所以要输出地址,所以要对buffer进行强制转换为void*,void*表示任意类型的指针,或表示“该指针与一地址值相关,
25 //但是不清楚在此地址上的对象的类型
26 for (int i = 0; i < N; i++)
27 pd2[i] = pd1[i] = 1000 + 20.0 * i; //对pd2和pd1指向的内存空间进行赋值
28 for (int i = 0; i < N; i++) //显示pd2和pd1指向的内存空间的值和地址
29 {
30 cout << "pd1[" << i << "] = " << pd1[i] << " at " << &pd1[i] << endl;
31 cout << "pd2[" << i << "] = " << pd2[i] << " at " << &pd2[i] << endl;
32 }
33
34 cout << endl;
35 cout << "Now calling new and new placement for second time" << endl;
36 cout << "重新为一个指针使用new定位符,为pd4定位到buffer数组的地址" << endl;
37 double *pd3, *pd4; //注意double* pd3,pd4只是声明了一个指针pd3
38 pd3 = new double[N];
39 pd4 = new (buffer) double[N]; //重新将buffer的地址赋给pd4,buffer的地址还是原来的地址
40
41 cout << "使用new给pd3分配内存,指针pd3(一个地址)为:" << pd3 << endl;
42 cout << "使用定位符new将buffer的地址赋给pd4" << endl;
43 cout << "使用new定位符给pd4分配内存,指针pd4(一个地址)为:" << pd4 << endl;
44 cout << "buffer的地址为:" << (void *)buffer << endl;
45
46 for (int i = 0; i < N; i++)
47 pd3[i] = pd4[i] = 1000 + 40.0 * i; //对pd2和pd1指向的内存空间进行赋值
48 for (int i = 0; i < N; i++) //显示pd2和pd1指向的内存空间的值和地址
49 {
50 cout << "pd3[" << i << "] = " << pd3[i] << " at " << &pd3[i] << endl;
51 cout << "pd4[" << i << "] = " << pd4[i] << " at " << &pd4[i] << endl;
52 }
53
54 cout << endl;
55 cout << "Now calling new and new placement for third time" << endl;
56 delete[] pd1; //释放常规new运算符分配的内存
57 pd1 = new double[N]; //重新给pd1分配一块内存
58 pd2 = new (buffer + N * sizeof(double)) double[N]; //提供一个从数组buffer开头算起的偏移量
59
60 cout << "使用new给pd1分配内存,指针pd1(一个地址)为:" << pd1 << endl;
61 cout << "使用定位符new将buffer的地址赋给pd2" << endl;
62 cout << "使用new定位符给pd2分配内存,指针pd2(一个地址)为:" << pd2 << endl;
63 cout << "buffer的地址为:" << (void *)buffer << endl;
64
65 for (int i = 0; i < N; i++)
66 pd2[i] = pd1[i] = 1000 + 20.0 * i; //对pd2和pd1指向的内存空间进行赋值
67 for (int i = 0; i < N; i++) //显示pd2和pd1指向的内存空间的值和地址
68 {
69 cout << "pd1[" << i << "] = " << pd1[i] << " at " << &pd1[i] << endl;
70 cout << "pd2[" << i << "] = " << pd2[i] << " at " << &pd2[i] << endl;
71 }
72
73 delete[] pd1; //释放常规new运算符分配的内存
74 delete[] pd2; //释放常规new运算符分配的内存
75 //关于是否使用delete释放new定位符分配的内存的问题:
76 //如果此处使用delete [] pd3; 将会导致程序报错
77 //因为buffer指定的内存是进来内存,而delete只能用于删除这样的指针:指向常规new运算符分配的堆内存
78 //也就是说数组buffer位于delete的管辖之外。
79 //因为buffer是在函数外部定义的数组变量,所以buffer的存储方式为静态存储,在函数内定义的变量为自动存储
80 //在函数外部定义或者是使用关键字static定义的变量存储方式都为静态存储
81 system("pause");
82 return 0;
83 }
程序说明:
01)void*表示任意类型的指针,或表示“该指针与一地址值相关,但是不清楚在此地址上的对象的类型,程序中使用了(void *) buffer; 对buffer进行了强制转换,
以便于cout<<buffer<<ebdl; 显示的是地址,而不是字符串;
02)关于是否使用delete释放new定位符分配的内存的问题(怎么样释放由new定位运算符分配的内存,见第十二章):
如果此处使用delete [] pd3; 将会导致程序报错
因为buffer指定的内存是进来内存,而delete只能用于删除这样的指针:指向常规new运算符分配的堆内存
也就是说数组buffer位于delete的管辖之外。
因为buffer是在函数外部定义的数组变量,所以buffer的存储方式为静态存储,在函数内定义的变量为自动存储
在函数外部定义或者是使用关键字static定义的变量存储方式都为静态存储
执行结果:
名称空间特性
01)通过定义一种新的声明区域来创建命名的名称空间,这样做的目的之一是提供一个声明的区域。一个名称空间中
的名称(变量)不会与另外一个名称空间的相同名称发生冲突,同时允许程序的其他部分使用该名称空间中的声明
的东西。例如,下面代码使用关键字namespace创建了两个名称空间:Jack和Jill
namespace Jack
{
double pail; //声明变量
void fetch(); //声明函数
int pal; //声明变量
struct Well {...}; //声明结构Hill
}
namespace Jill
{
double bucket(int n) //函数定义
{
...
}
double fetch; //声明变量
int pal; //声明变量
struct Hill {...}; //声明结构
}
02)名称空间可以是全局的,也可以位于另一个名称空间中,但是不能位于代码块中。因此默认情况下,在名称空间中
声明的变量的链接性为外部(除非它引用了常量)
03)任何名称空间中的名称都不会与其他名称空间中的名称发生冲突。因此Jcak中的变量fetch和Jill中的fetch共存
04)名称空间是开放的,可以把名称加入到已有的名乘客空间中,如下将名称goose添加到Jill中已有的名称列表中:
namespace Jill
{
char* goose (const char*); //在已有的名称空间中添加一个函数声明,返回值为char型指针
}
05)同样在Jack名称空间为fetch()函数提供了原型,可以在该文件后面(或另外一个文件中)再次使用Jack名称空间
来提供该行拿书定义的代码:
namespace Jack
{
...
}
06)如何访问名称空间中的变量?最简单的方法是通过作用域解析运算符::,使用名称空间来限定该名称,如下:
Jcak::pail = 12.34; //使用Jack名称空间中的名称(变量)pail
Jill::Hill mole; //使用Jill名称空间中的名称(结构或者是结构新类型)Hill声明一个结构mole
Jcak::fetch(); //调用Jack名称空间中的函数fetch()
07)我们并不希望每次使用每次时都对它进行限定(名称空间名+::+变量形式),因此c++提供了using声明和using
编译指令。
A、 using声明使特定的标识符可用,使用方法如下:
using Jill::fetch;//a using declaration,使用该声明之后,就可以在某一个函数中直接使用fetch,而不用再使用Jcak::fetch
例1: 在函数内使用using声明:只是在该函数内可用
namespace Jill
{
double fetch; //声明变量
int pal; //声明变量
}
int main()
{
using Jill::fetch; //在main()函数中就可以直接使用fetch变量,而不用再次使用Jill::fetch
//此时的fetch为局部变量(或将fetch添加到局部名称空间中)
//局部的fetch也将覆盖全局的fetch(如果有全局变量fetch的话)
double fetch; //不合法!,因为已经有一个局部变量fetch变量了
cin>>fetch; //读一个值到Jill::fetch
}
例2:在函数外使用using声明:将把名称添加到全局名称空间中
void other();
namespace Jill
{
double fetch; //声明变量
int pal; //声明变量
}
using Jill::fetch; //此时的fetch为全局变量(或将fetch添加到全局名称空间中)
int main()
{
cin>>fetch: //读一个值到Jill::fetch
other();
...
}
void other()
{
cout<<fetch; //输出Jill::fetch
...
}
B、 using编译指令使用方法(using编译指令使所有的名称都可用,using声明只可以使一个名称可用):
using namespace Jcak; //使Jack名称空间中的所有名称都可用
08)变量Jacl::pal和Jill::pal是不同的标识符,表示不同的内存单元。然而使用using声明,情况将会发生变化:
using Jack::pal;
using Jill::pal;
pal = 4; //此处将会导致二义性,到底使用哪个名称空间中的pal呢?
事实上,编译器不允许程序员同时使用上述两个using声明,因为这将导致二义性
09)在函数内使用using编译指令,全局变量 vs 局部变量 vs 名称空间中的变量 (这三个变量的名字都一样)
namespace Jill
{
double bucket(double n) {...} //定义一个函数
double fetch; //声明一个变量
struct Hill {...}; //声明一个结构
}
char fetch; //声明一个全部变量fetch
int main()
{
using namespace Jill; //名称空间Jill中的名称将作为局部变量,添加到mian()
Hill Thrill; //使用结构新类型新建一个结构
double water = bucket(2.0); //调用名称空间Jill中定义的一个函数
double fetch; //不会导致错误,定义局部变量fetch,注意和上边使用using声明比较(例1)
//且局部定义的fetch将会覆盖掉全局fetch和Jill::fetc
//但是如果使用using声明,在main()中声明了Jill::fetch,再去定义局部变量fetch将会出错
cin>>fetch; //读取一个值给局部变量fetch
cin>>::fetch; //读取一个值给全局变量fetch
cin>>JIll::fetch; //读取一个值给全局变量JIll::fetch
}
int foom()
{
Hill top; //不合法
Jill::Hill creat; //合法
}
说明:I 在mian()中,名称Jill::fetch被放在局部名称空间中,但其作用域不是局部的,因此
不会覆盖全局的fetch。局部声明的fetch将会覆盖掉全局fetch和Jill::fetch。
然而,如果在main()中不使用using编译指令,使用作用域解析运算符::(Jill::fetch),
则全局fetch和Jill::fetch都是可用的,但是人继续去定义局部变量fetch将会报错。使用using编译指令
将不会导致这样的错误,局部版本将隐藏名称空间版本。(即本例和例1进行了比较)
II 虽然函数中的using编译指令将名称空间的名称视为在函数之外声明的,但它不会使得该文件
中的其他函数能够使用这些名称,比如foom()函数中不可以直接使用名称空间Jill中的结构Hill
10)#include <iostream> //将iostream放到名称空间std中
using namespace std; //using编译指令指出std名称空间在全局可用
main()
{ ... }
其中#include <iostream> //将iostream放到名称空间std中
using namespace std;
等价于#include <iostream.h>
11)可以将名称空间进行嵌套:
namespace elements
{
namespace fire
{
int flame;
}
float water;
}
使用内部的flame方法为:using namespace element::fire::flame;
同样可以使用下面的using编译指令使内部的名称可用:
using namespace element::fire;
12)可以在名称空间中使用using声明和using编译指令,如下所示:
namespace myth
{
using Jill::fetch; //使用名称空间Jill中的名称(变量)fetch
using namespace elemebts; //使用名称空间elements下的所有名称
using std::cout: //使用名称空间std中的cout
using std::cin; //使用名称空间中的cin
}
假设现在要访问Jill::fetch,由于Jill::fetch现在在名称空间myth中(在myth中,它被叫做fetch)
因此可以这样访问它:std::cin>>myth::fetch; //读数据到myth::fetch
当然,由于fetch也位于Jill名称空间中,因此也可以称作JIll::fetch
std::cout<<Jill::fetch; //显示读进myth::fetch中的值
当然如果没有与之冲突的局部变量,则也可以这样做:
using namespace myth;
cin >> fetch;
另外下面的语句将导入名称空间myth和elements(因为elements位于myth中)
using namespace myth;
13)可以给名称空间创建别名
namespace muft = my_very_favorite_things; //表示让mvft成为my_very_favorite_things的别名
可以使用这种技术来简化对嵌套名称空间的使用:
namespace MEF = myth::elements::fire;
using MEF::flame;
14)未命名的名称空间: 其内部的名称作用域为本cpp文件,其他cpp文件不可访问名称空间中的名称,这就提供
了链接性为内部的静态变量的替代品,例如:
static int counts; //声明一个链接性为内部的静态变量(这里静态的意思不是counts的值不可改变)
int other(); //声明一个函数
int mian()
{
...
}
int other()
{
...
}
以上代码等价于:
namespace
{
int counts; //没有名字的名称空间中的变量,其链接性为内部,即只能够在本cpp文件内使用counts
}
int other(); //声明一个函数
int mian()
{
...
}
int other()
{
...
}
名称空间示例
namesp.h
1 #pragma once //自动添加进来的,表示仅编译一次
2 //相同作用命令
3 //#ifndef ABC_H
4 //#define ABC_H
5
6 //namesp.h
7 #include <iostream>
8 #include <string>
9
10 namespace pers //新建一个名称空间
11 {
12 struct Person //新建一个结构新类型
13 {
14 std::string fname; //新建一个名字为fname的string类变量
15 std::string lname; //新建一个名字为lname的string类变量
16 };
17 void getPerson(Person & rp); //声明一个函数,形参为指向Person结构的引用变量rp
18 void showPerson(const Person &rp); //声明一个函数,形参为指向Person结构的引用变量rp
19 }
20 namespace debts
21 {
22 using namespace pers; //使用using编译指令,使名称空间pers中的名称在debts中可用
23 struct Debt
24 {
25 Person name; //使用名称空间pers中的结构新类型新建一个结构name
26 double amount; //新建一个double型变量amount
27 };
28 void getDebt(Debt & rd); //声明一个函数,形参为指向Debt结构的引用变量rd
29 void showDebt(Debt & rd); //声明一个函数,形参为指向Debt结构的引用变量rd
30 double sumDebts(const Debt arr[], int n); //声明一个函数,第一个形参为结构数组arr
31 }
1 //子函数namesp.cpp
2 #include <iostream> //用尖括号表示在系统文件中查找
3 #include "namesp.h" //用双引号表示在自己新建的h文件中查找
4
5 namespace pers
6 {
7 using std::cout;
8 using std::cin; //使用using声明,表示以后可以使用cin
9 void getPerson(Person & rp) //函数定义,在h文件中只是声明了一下
10 {
11 cout << "Enter the first name: ";
12 cin >> rp.fname;
13 cout << "Enter the lats name: ";
14 cin >> rp.lname;
15 }
16 void showPerson(const Person &rp) //提供函数定义
17 {
18 std::cout << rp.lname << ", " << rp.fname; //std::cout表示直接使用运算符::来访问名称空间中的名称
19 }
20 }
21 namespace debts
22 {
23 void getDebt(Debt & rd) //提供函数定义
24 {
25 getPerson(rd.name); //调用名称空间pers中的getPerson(Person & rp)函数,
26 //name是名称空间debts下结构Debt中的一个成员,而name是pers名称空间下由Person结构创建的
27 //debts和pers的联系就是可以在debts中使用pers中的名称的那一句using编译指令
28 std::cout << "Enter debt: ";
29 std::cin >> rd.amount;
30 }
31 void showDebt(Debt & rd) //提供函数定义
32 {
33 showPerson(rd.name); //调用名称空间pers中的showPerson(Person & rp)函数,
34 std::cout << ": $" << rd.amount << std::endl;
35 }
36 double sumDebts(const Debt arr[], int n)
37 {
38 double total =0; //局部变量必须初始化,否则会报错的
39 for (int i = 0; i < n; i++)
40 total += arr[i].amount;
41 return total;
42 }
43 }
1 //main()
2
3 #include <iostream>
4 #include "namesp.h"
5
6 void other(void); //声明一个函数
7 void another(void); //声明一个函数
8
9 int main()
10 {
11 using debts::Debt; //使用using声明,使名称空间中的新结构类型Debt可用
12 using debts::showDebt; //使用using声明,使void showDebt(Debt & rd)函数可用
13
14 Debt golf = { {"Benny","Goatsniff"},120.0 }; //使用新结构类型Debt创建结构golf并初始化
15 showDebt(golf); //调用名称空间debts中的函数
16 other();
17 another();
18
19 system("pause");
20 return 0;
21 }
22
23 void other(void)
24 {
25 using std::cout;
26 using std::endl;
27 using namespace debts; //使用using编译指令,使名称空间debts中的名称在other()函数中都可用
28
29 Person dg = { "Doodles","Glister" }; //使用结构新类型Person创建一个结构dg并初始化
30 showPerson(dg); //调用名称空间pers中的函数showPerson(),而pers又在debts可用,所以只声明一个debts就可以了
31 cout << endl;
32 Debt zippy[3]; //使用结构新类型Debt创建一个结构数组zippy,包含了3个Debt型结构
33 for (int i = 0; i < 3; i++)
34 getDebt(zippy[i]); //首先是输入first name和last name,再输入debt
35 for (int i = 0; i < 3; i++)
36 showDebt(zippy[i]); // 首先是显示last name和first name ,再输出debt
37 cout << "Total debt: $" << sumDebts(zippy, 3) << endl; //调用名称空间debts下的函数double sumDebts(const Debt arr[], int n)
38 }
39
40 void another(void)
41 {
42 using pers::Person; //使用using声明仅使名称空间pers中的结构Person可用
43 Person collector = { "Milo","Rightshift" }; //使用结构新类型创建结构collector并初始化
44 pers::showPerson(collector); //使用作用域运算符::使用名称空间中的void getPerson(Person & rp)
45 std::cout << std::endl;
46 }
程序说明:涉及到了不同文件中的不同函数之间的函数调用
执行结果为:
名称空间的使用规范 m12
01)使用在已命名的名称空间中声明的变量,而不是使用外部全局变量
使用在已命名的名称空间中声明的变量,而不是使用静态全局变量
02)如果开发了一个函数库或者是类库,将其放在一个名称空间中。
03)不要在头文件中使用using编译指令。首先,这样掩盖了要让那些名称可用;另外,包含头文件的顺序可能会
影响程序的行为。如果非要使用using编译指令,应将其放在所有预处理器编译指令#include之后
04)导入名称时,首选使用作用域解析运算符或using声明的方法
05)对于using声明,首选将其作用域设置为局部而不是全局
2019.04.04 早 于 杭州
haijing miss you
来源:oschina
链接:https://my.oschina.net/u/4315513/blog/3593974