9.1单独编译
一个程序可以分为三部分:
- 头文件:结构体声明和函数原型
- 源代码文件:与结构有关的函数代码
- 源代码文件:调用与结构有关的函数代码
头文件中经常包括:函数原型,#define或const定义的符号常量,结构声明,类声明,模板声明,内联函数。因为这些都是与定义无关的内容,而内联函数有链接属性。普通函数定义不能放在头文件中,因为如果多个程序调用了该头文件,但都包含了同一个函数定义,会报错。
在一个文件中,头文件只能包含一次,为防止包含多次,可以使用预处理编译指令#ifndef(if not defined):
//头文件: ***.h
#ifndef COORDIN_H_
...
#endif
表示如果没有创建该名称,则创建,如果已有,则直接忽略,避免了多次创建。
在源程序中包括头文件时:
#include<iostream> //<>表示这是库文件
#include"***.h" //""表示这是我们自己创建的头文件
9.2存储持续性,作用域和连接性
c++11中使用四种不同方案来存储数据:
- 自动存储持续性:执行所属函数或者代码块时创建,执行完后内存被释放
- 静态存储持续性:函数外定义或者使用关键字static定义的变量,在程序运行整个过程都存在。
- 线程存储持续性:并行编程中讨论。
- 动态存储持续性:受用new运算符分配的内存将一直在,直到使用delete运算符释放内存。
作用域描述了名称在文件的多大范围可见(局部变量,全局变量)。
链接性描述了名称如何在不同单元间共享。链接性为外部的名称可以在文件中共享,链接性为内部的名称只能由一个文件中的函数共享
这部分知识不怎么了解,平时用不到,也就不理解,等以后接触到了,再补充详细的。
可以在一个程序引用另一个程序的变量
//1.cpp
int nums = 10; //函数外部,表示全局变量
//2.cpp
extern int nums; //表示从外面引入该变量
如果一个变量名,有一个全局版本,一个局部的,在函数局部只会表现出局部的版本,但如果想在局部打印全局值,在变量名前加一个解析运算符(::)
cout<< ::nums <<endl; //表示其全局版本
在函数外声明的变量(全局变量)前加上static,表示该变量只适用于本cpp文件,而不能被其他文件引用:
static int mun = 10;
int main()
{
....
}
如果是在某个函数中声明变量(局部变量)时,前面加static,表示该变量为静态变量,并且,无论该函数被调用几次,该变量只会在第一次调用时初始化,第二次调用时,该变量值不再是规定的初始化的值,而是上次调用后经变化的值,以此类推,也就是说,该变量不会因为函数调用结束而释放其内存,而是会一直存在。:
void function(int a)
{
static int n = 0;
int m = 0;
n += a;
m += a;
}
上述函数调用结束时,变量m内存释放,下次调用时再次初始化为0,但n不同,即使函数调用结束,它依旧在,并且值为改变后的,当再次调用时,它的值不会初始化为0,而是之前的n+a。
说明符和限定符
或称之为存储说明符和cv-限定符。
下面是存储说明符:
- auto(C++11不再是说明符):自动类型推断
- register:声明中指示寄存器存储(提高访问速度)
- static:作用在整个文件的声明时,表示内部链接性,即其他文件不可访问。局部声明中,表示存储持续性为静态的。
- extern:表明是引用变量
- thread_local:不楚啥玩意
- mutable:根据const解释含义
下面是cv-限定符:
- const:不可修改
- volatitle:表明,即使程序代码没有对存储单元进行修改,其值也可能会发生变化(不太理解,等用到的时候在详细解释)
回到mutable,它表示即使结构或者类变量为const,某个成员依然可以被修改:
struct student{
char name[30];
mutable int scores;
};
const student s1 = {"kyle",10};
strcpy(s1.name,"smith"); //不可以,错误
s1.scores++; //可以
const之前都是表示常量,但对默认存储类型有些影响,默认情况下全局变量的链接性为外部的,但const全局变量的链接性为内部的,就像使用了static一样,因为,如果在某个头文件中声明const常量,且同一个程序多个文件引用该头文件,即所有文件都会包括该常量的定义,如果链接性为外部,即多个文件声明同一个变量,显然不满足单定义原则,因此表示链接性为内部,即虽然同一个变量名多个文件同时定义,但互不影响。
同变量一样,函数也有链接性。所有函数的存储持续性都为静态的,一直存在,默认情况下,链接性为外部的,可以在文件中共享,可以在原型前使用extern表示该函数是在另一个文件中定义的,可以使用关键字static规定函数链接性为内部的,只能在一个文件中使用。
语言链接性,在函数重载时,虽然我们定义函数名称都相同,但c++编译器执行名称矫正或名称修饰,为重载函数生成不同的符号名称。这里有c语言链接性与c++语言链接性之分。
new运算符,之前只有new运算符开辟一段空间,但不能初始化,现在可在类型后面加上初始值:
int *pi = new int (6);
同理可以给结构体,数组赋值。
如果new找不到请求的内存量,则返回空指针,或者发现异常。并且,new和delete使用时都是调用了相关函数实现其功能的。
定位new运算符:通常new运算符在堆中找到内存块,还有另一种形式,定位new运算符,可以使用我们所指定的位置,要使用该特性时,需要包含头文件new,
char nums [100]; //提前定义一部分内存位置
...
char *p;
p = new (nums) char[5]; //表示在nums内存中开辟出我们所需要的空间
并且要注意,普通的new运算符后需要delete释放内存,但定位new运算符不需要,因为它是在已经开辟的内存中找到相应内存存储数据,所以不需要释放内存。
名称空间
名称空间就是类似于多个不同的区域,在项目中,每个人写的代码不同,但有可能有相同的函数,变量等等,整合就会出现错误,因此采用不同的名称空间,防止名称冲突。
使用关键字namespace创建名称空间:
namespace mk{
int num;
double das;
} //创建名称为mk的名称空间,其中包含两个变量。
名称空间可以时全局的,也可以存在另一个名称空间中,但不能位于代码块中,默认情况下,名称空间链接性都是外部的,其他文件可以使用。除了我们定义的名称空间外,还有另一种----全局名称空间,对应文件级声明区域,之前所提得到全局变量都在全局名声空间中。访问名称空间中名称的方法,最简单的就是使用作用域解析运算符:
mk::num = 10;
这样称之为限定的名称,但每次使用太麻烦了因此可以使用using声明和using编译指令。
using声明:
using mk::num; //表示引用mk名称空间中的num变量
可以放在函数中,就是一个局部变量,因此函数中不能有与之名称相同的变量,放在函数外,就是全局变量。
using编译指令
using namespace mk; //表示调用名称空间mk中的所有名称
这样所有该名称空间名称都为全局的,无论是在函数中函数文件头,这一点与using声明不同,因此,这样可以在函数中声明与名称空间名称相同的变量,二者关系就是局部变量与全局变量(名称空间中的)。
此外,名称空间还支持嵌套,即在一个名称空间中创建另一个名称空间,也可以在名称空间中使用using声明和using编译指令。
同时可以创建没有名称的名称空间:
namespace{
...
}
这样该名称空间只能在本文件中使用,就像一个全局名称空间,因为没有名称,所以不能被其他文件引用。