我们在写程序的时候,为了修改和编译方便。
通常会把我们的程序分为几个部分
1、头文件,里面应该包含了很多数据结构和函数的声明
2、源代码文件。这里面应该包含了程序中所有的函数定义,但是仅仅只是定义
3、这里面包含的有main函数,还有一些调用我们之前定义函数的代码。
C++根据变量的存储持续性分为自动的,静态的,线程的,动态的。
作用域:指的是一个变量在多大的范围内是可用的。
连接性:表述了一个变量在什么范围能够被共享
变量的作用域分为局部和外部。例如函数原型中的参数列表中的变量,他们的作用域就是函数原型的那个参数列表,所以基本来说没什么用,这样一来函数原型中的参数列表中的参数名字无论是什么都无所谓了。
自动存储持续性:在函数中或者一个代码块中声明的函数参数和变量的存储持续性为自动的,作用域是局部的,就是这个函数或者这个代码块,当我们执行完这个函数和代码块的时候这个变量也就会被释放了。
并且,如果一个函数与外部定义了一个同名的变量,那么在这个函数里面的时候,里面的定义是会自动屏蔽掉函数外面的定义的,也就是说,虽然函数内外都有同名的变量,但是实际上他们是两个变量。当执行完这个变量的时候,外部定义的变量会重新的显现出来。
自动变量是存储在栈内的,因为自动变量一般存在于局部,比如一个函数或者一个代码块,每一次的函数调用都会为这个函数在内存的栈中开辟一段内存空间用来存放这个函数内部定义的自动变量,当函数调用完成的时候,系统会根据两个指针,帧指针与栈指针将定义的变量释放掉。栈结构中,数据的进出顺序为先进后出,后进先出,这方便了参数的传递。
并且在一个函数或者代码块执行完之后,并不是真正的释放了之前局部变量占有的内存,仅仅是把两个栈指针恢复为执行函数之前的样子 。他们之前使用的空间将会被下一个函数覆盖使用。
静态持续变量:静态持续变量的和自动变量的主要区别就是生存期。只要是定义了一个静态变量的话,那么他们在整个程序执行期间将一直存在于内存, 因为自动变量和静态变量的生存期不一样,所以静态变量是不用存在于栈中的,内存中有一段专门的静态存储区来存储静态变量。静态变量在没有显示初始化的时候都会被初始化为0。
静态变量也有一个自己的特性就是连接性,自动变量是没有连接性的,因为自动变量本身就不能被他的作用域意外的代码共享。
静态变量的连接性分为:外部(其他文件也可见),内部(整个程序可见,只有所在的文件可见),无连接性(只有局部可见)。
外部:直接在所有的函数外部定义一个普通变量,也就是我们常说的全局变量,这样的变量在一个程序有很多文件的时候,在每一个文件都是可见的。
内部:也是必须在函数外部进行定义,但是要加上关键字static,此时这个变量只是在它所在的为文件是可见的, 在这个程序的其他文件中还是不可见的。
无连接性:要加上static在函数内部进行定义,其实此时这个静态变量只不过是一个生存期比较长的自动变量而已。
静态变量在定义之后要进行初始化:
静态的,动态的。
0初始化和常量表达式初始化被声明为静态初始化,这些初始化时在程序的编译阶段完成的。
如果在给静态变量赋值的时候用到了某些库函数,那么这种初始化就叫做动态初始化。
连接性为外部的变量叫做外部变量,他的存储持续性为静态的,作用域是整个文件,并且也可以在这个程序中的其他文件访问。外部变量也叫做全局变量。
声明一个变量分为两种形式,一种是定义声明,一种是引用声明。定义声明在声明的同时会给这个变量分配内存,可以初始化。由于C++规定在一个程序中一个 变量只能被定义一次(但是自动变量在不同的作用域是可以定义同名的变量的,他们有着不同的内存),那么在引用声明一个变量的时候,加上extern,它就证明这声明了一个已经存在的变量,不会为这个变量再次申请内存空间。并且在引用声明一个变量的时候,不能对他进行初始化,否则和正常定义一个变量是一样的。
如果在多个文件中使用外部变量,那么在一个文件中定义这个外部变量的时候要用extern来修饰定义,当然,在定义声明这个变量的文件中,对该外部变量的定义可以省略extern这个关键字,但是,在其他想用这个外部变量的文件中,都会使用extern来进行一个引用声明,以便于能够用到这个外部变量。
在一个程序中,如果要想在一个函数内部使用外部变量的话,可以在函数中用extern声明一下已经存在的外部变量,或者用::作用域解析运算符来进行引用外部变量的版本,后一种在函数内部定义了和全局变量同名的变量的情况很好用。
如果在一个文件中定义了一个普通的外部变量,然后在另一个文件中想定义一个同名的其他变量。因为常规的外部变量都有外部连接性,如果你直接在另外一个文件中定义一个同名的变量的话,就会出现一个程序同一个变量定义两次的错误。但是如果在另一个文件中定义这个变量的时候加入static来进行修饰,那么这个静态变量将屏蔽掉常规的外部变量,因为新定义的这个变量的连接性为内部,只能在定义它的文件内部来使用。连接性为内部,那么只能被本文件中使用,如果为外部则这个程序的所有文件都能使用。
静态变量的第三个连接性是无连接性,这种静态变量一般定义在函数的局部,称为静态局部变量。静态局部变量和普通的局部变量并没有太大的差别,只是生存期上有不同。另外,如果在函数内部定义静态局部变量并且初始化的时候,这个初始化的操作只会执行一次,以后再调用这个函数的时候,不会在执行初始化语句,直接用这个静态变量内部存好的数据。
关于限定符:
mutable这个限定符是和const配合起来使用的,当const指出一个结构是不可改变的时候,可以用mutable这个关键字来修饰这个结构定义里面的成员使得这个成员即使整个结构体不能变但是他也可以变。
const还有一个比较特殊的用法,但是仅仅是对c++来说的。在定义外部变量的时候,常规的情况这个外部变量的连接性是外部的,但是如果加上了const来定义一个外部变量的话,那么这个变量的连接性就为内部的,和加了static申请的一样。这一点在常量定义在头文件中,很多源文件引用头文件的时候用处很大,编译预处理会把头文件中的所有的常量定义都会复制到每一个引用这个头文件的源文件中,如果不在定义的常量上面添加const那么就会出现一个常量同时定义在一个程序的不同的文件中,这样显然是有问题的,因为即使是常量也是有着外部连接性的,但是如果在头文件中定义的常量是用const来进行修饰的话,那么这样每一个文件中的常量的连接性都是内部可见的,这样就 不会出现上面的那种错误。这就意味着,即使每一个头文件都有一套相同的常量,但是这套常量是每个人有一套,不是属于自己的。
函数的连接性都默认为外部的,因为函数没有任何内部的概念。如果想让其连接性为内部,可以用static在函数的声明和定义部分都加上相应的修饰。
名称空间:
声明区域:全局变量是整个文件,局部变量是一个代码块
潜在作用域:从声明点开始,一直到声明区域结束,通常比声明区域要小一点。之所以说潜在,就是说,即使是在潜在作用域,那么这个变量也不一定能够可见。因为可能有些局部变量会屏蔽掉这些。
作用域:变量对程序可见的区域
感觉所谓的名称空间,就是一个区域,没个 区域即使有同名的变量也不是相同的,并且,每一个空间里面的变量都是属于这个空间的。
名称空间可以由用户自己来进行定义,所谓的定义一个新的名称空间,就是要创建一个新的声明区域,用来声明一些变量,不同的命名空间中的变量可以 相同,不会互相干扰。同时还允许一个名称空间嵌套在另外一个名称空间中。名称空间一般都定义在外部,不能够定义在函数或者代码块的内部。名称空间是开放的,可以在定义之后不断的利用namespace 名称空间名{新声明}这种形式来为名称空间增加声明。名称空间里面可以包含函数的定义代码。并且在引用不同名称空间中的成员时,利用作用域解析运算符::来对引用的变量的名称空间进行限定。。
现在变量的名称分为两种,一种是被名称空间限定修饰的,另外一种是什么也不加的。比如
std::cout ,cout
如果我们在实际的程序中需要是用一个我们自定义的名称空间中的一个变量,那么能否有一个办法可以省略掉变量前面的名称空间的修饰呢。就是要用到using声明或者using编译指令。例如
namespace haha
{
int a;
int b;
}
一般来说我们可以用haha::a的形式来使用a这个变量
如果我们只想不加修饰符的使用a这个变量,那么我们就用using 声明的形式
using haha::a;
在代码中写上这一句之后,就可以想平常的变量一样使用a了不用加上haha::。using声明将特定的名称添加到他的声明区域中。这种声明指令出现在函数中,那么此时的a变量就和普通的局部变量一样,如果申明在函数外面,就和全局变量有一样的性质。
如果想正常使用整个命名空间的所有东西,那么using声明指令肯定是不行了,就要用到using编译指令。
using 名称空间;例如
using haha;这样一来,haha空间中的所有的变量都可以在相应的声明区域中使用,且不用加上作用域标识符。
using指令声明一个命名空间中的变量,那么在这条指令所在的声明区域中,不能够再出现同名的变量,那样的话就会造成重复定义的错误。
using指令和using编译指令的区别:
1、using指令用于名称空间中一个特定的变量,而using编译指令用于整个名称空间。
2、如果一个函数内部已经定义了一个a变量,在用using指令声明一个同名变量的话,会造成重复定义的错误。但是如果使用了using编译指令,那么即使是在函数内部声明的名称空间,也是相当于在函数外部声明的,所以如果此时在函数内部自己定义一个同名的变量,就会按照局部屏蔽外部的原则,不使用名称空间中的变量。
虽然,在函数内部用using编译指令相当于在函数外部声明名称空间,但是这种外部性是伪外部性,也就是说,文件中的其他函数是不能够使用这个名称空间中的任何声明的变量的。
using编译指令具有传递性,也就是说,可以通过名称空间嵌套的方式,将一些列我需要的名称空间综合在一起。
甚至有的时候我们可以给名称空间创建别名
比如:namespace 别名 = 另一个名称空间的名字
如果我们想单纯的使用嵌套名称空间内的某一个空间,可以根据他们的层次关系,用作用域标识符来进行逐层引用。
3、我们可以定义一个没有名字的名称空间,没有名字的名称空间只能在本文件中使用,因为没有名字所以不能再其他文件中引用。间接的提供了内部连接性。