zoukankan      html  css  js  c++  java
  • C++第九章__将原来的程序分为三部分:头文件、主函数和子函数__存储类型、作用域和链接性__自动存储持续性__静态持续变量&static的两种用法__静态持续性、外部链接性__静态持续性、内部链接性__静态存储持续性、无链接性__存储方案和动态分配__定位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 }
    external.cpp
     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 }
    surpport.cpp

    程序说明:

      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 }
    twofile1.cpp 该文件件包含了main()
     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 }
    twofile2.cpp

    执行结果:

     静态存储持续性、无链接性(涉及到一个应用,要看一下的) 

    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个字符,有一个位置要为空字符''保留
    16 
    17     while (cin)  //如果输入的字符数小于ArSize,那么最后的回车键会被保留在输入流中。
    18     {            //如果输入流中有字符,那么cin的返回值一直是true(因为没有接收的变量)
    19         cin.get(next);  //如果输入的字符多于9个,那么输入到next中去
    20         while (next != '
    ')
    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 != '')
    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 }
    使用new定位符的例程

    程序说明:

    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 }
    namesp.h
     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 }
    namesp.cpp
     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 }
    namesp_main.cpp

    程序说明:涉及到了不同文件中的不同函数之间的函数调用

    执行结果为:

    名称空间的使用规范 m12

    01)使用在已命名的名称空间中声明的变量,而不是使用外部全局变量
      使用在已命名的名称空间中声明的变量,而不是使用静态全局变量
    02)如果开发了一个函数库或者是类库,将其放在一个名称空间中。
    03)不要在头文件中使用using编译指令。首先,这样掩盖了要让那些名称可用;另外,包含头文件的顺序可能会
      影响程序的行为。如果非要使用using编译指令,应将其放在所有预处理器编译指令#include之后
    04)导入名称时,首选使用作用域解析运算符或using声明的方法
    05)对于using声明,首选将其作用域设置为局部而不是全局

    2019.04.04 早 于 杭州

    haijing miss you

  • 相关阅读:
    Android ImageView设置图片原理(下)
    C++ 虚函数表 多重继承
    C++ 虚函数表 单继承
    私有继承
    内联函数和宏定义的区别
    #pragma pack(x) CPU对齐
    static 变量(静态变量)
    C++ 中const作用
    如何连接宏参数
    几种常见容器比较和分析 hashmap, map, vector, list ...hash table
  • 原文地址:https://www.cnblogs.com/YiYA-blog/p/10619306.html
Copyright © 2011-2022 走看看