本章主要包括:
- 单独编译;
- 存储持续性、 作用域和链接性;
- 定位(placement) new 运算符;
- 名称空间。
9.1 单独编译
-
有什么用:可以将程序的组件函数放在独立的一个文件中,当只修改了这一个函数,只需
单独编译
该函数文件,然后将它与其他文件的编译版本连接在一起,形成一个可执行的程序,这样使得大型程序管理更加方便。 -
有好也有坏: 例如,2个文件里的函数都用到了同一个结构声明,那么两个文件都需要包含该声明,并且对其中一个文件的声明做了修改,就必须对其他文件内同一个声明做相同的修改。这样难免会遗漏和修改错误。
-
头文件: 与其每个文件都加入声明,不如将声明放入头文件中,然后使用
#inclide
在每一个函数文件中包含该头文件。这样,要修改结构体声明,只需修改头文件即可。因此我们现在可以将原来的程序分为三个部分:- 头文件: 包含结构体声明和使用这些结构的函数原型;
- 源代码文件: 包含与结构有关的函数代码;
- 源代码文件: 包含调用与结构体相关的函数代码。
-
头文件应当包含的内容:
- 函数原型
- 符号常量(#define 或 const)
- 结构声明
- 类声明
- 模板声明
- 内联函数
-
如何使用头文件和源代码文件:
- 项目只需包含源代码文件,而用
#include
管理头文件。 - 不要使用
#include
来包含源代码文件,不然会导致多重声明。 - 包含头文件时,应使用
""
而不是<>
,因为这样编码器会优先在当前工作目录中查找头文件。
- 项目只需包含源代码文件,而用
// coordin.h(头文件)
#include<iostream>
#ifndef COORDIN_H_
#define COORDIN_H_
struct polar
{
double distance;
double angle;
};
struct rect
{
double x;
double y ;
};
polar rect_to_polar( rect xypos);
void show_polar(polar depos);
#endif
//源代码文件(包含与头文件数据类型有关的函数代码)
#include <iostream>
#include "coordin.h"
#include <cmath>
polar rect_to_polar( rect xypos)
{ using namespace std;
polar answer;
answer.distance = sqrt( xypos.x*xypos.x + xypos.y*xypos.y);
answer.angle = atan2(xypos.y, xypos.x);
return answer;
}
void show_polar( polar depos)
{
using namespace std;
const double Red_to_deg = 57.29577951;
cout<< "distance = " <<depos.distance;
cout<< ", angle = "<< depos.angle*Red_to_deg;
cout<< " degress.\n";
}
//源文件(包含调用与结构体相关的函数代码)
//file1.cpp
#include <iostream>
#include "coordin.h"
int main()
{
using namespace std;
rect rplace;
polar pplace;
rplace.x = 1.5;
rplace.y = 1.5;
pplace = rect_to_polar(rplace);
pplace;
show_polar(pplace);
cout<< "Bye!\n";
while( cin>>rplace.x>> rplace.y)
{
pplace = rect_to_polar(rplace);
show_polar(pplace);
cout<< " Next two numbers: ";
}
return 0;
}
distance = 2.12132, angle = 45 degress.
Bye!
1 1
distance = 1.41421, angle = 45 degress.
Next two numbers:
2 2.5
distance = 3.20156, angle = 51.3402 degress.
Next two numbers:
5
5
distance = 7.07107, angle = 45 degress.
Next two numbers:
1
1
distance = 1.41421, angle = 45 degress.
Next two numbers:
q
9.2 存储持续性、作用域和连接线
c++使用四种不同的方案存储数据,区别在于数据保留在内存中的时间
-
自动存储持续性: 在程序执行到其所属的代码块时被创建,在执行完代码块后被释放。
-
静态存储持续性: 在函数外定义的或使用关键字
static
定义的变量,在程序整个运行过程中都存在。 -
线程存储持续性: 在并行编程中,由关键词
thread_local
声明的变量。它在其所属的线程中一直存在。 -
动态存储持续性: 由
new
运算符分配内存开始,到delete
运算符释放其内存结束。也被称为自由存储或堆。
9.2.1 作用域和链接
- 作用域: 名称在文件的多大范围内可使用。
- 局部作用域:只在定义它的代码块中可用;
- 全局作用域:在名称声明开始到文件结尾都可用。
- 函数原型作用域: 只在参数列表括号内可用,所以函数原型参数有无名称都行。
- 类中声明的成员作用域为整个类。
- 链接性: 名称如何在不同单元间共享。
- 内部的:只能由一个文件中的所有函数共享;
- 外部的: 可以在多个文件中共享。
9.2.2 自动存储持续性
函数中声明的函数参数和变量的持续性都为自动的,作用域为局部,没有链接性。
- 程序通过栈来管理自动变量。后进先出。
- 同名的自动变量,新定义的代替旧定义的。
#include <iostream>
void oil(int x)
{
using namespace std;
int a = 6;
cout<< "In oil(), a = " << a << ", &a = " << & a << ".\n";
cout<< "In oil(), x = " << x << ", &x = " << & x << ".\n";
{
int a = 7;
cout<< "In block(), a = " << a << ", &a = " << & a << ".\n";
cout<< "In block(), x = " << x << ", &x = " << & x << ".\n";
}
cout<< "pos_a = " << a << ", &a = " << & a << ".\n";
}
int main()
{
using namespace std;
int x = 5;
int y = 4;
cout<< "In main(), x = " << x << ", &x = " << &x << ".\n";
cout<< "In main(), y = " << y << ", &y = " << &y << ".\n";
oil(x);
cout<< "In main(), x = " << x << ", &x = " << &x << ".\n";
cout<< "In main(), y = " << y << ", &y = " << &y << ".\n";
return 0;
}
In main(), x = 5, &x = 0x7f74a34f502c.
In main(), y = 4, &y = 0x7f74a34f5030.
In oil(), a = 6, &a = 0x7ffc159d4808.
In oil(), x = 5, &x = 0x7ffc159d480c.
In block(), a = 7, &a = 0x7ffc159d4804.
In block(), x = 5, &x = 0x7ffc159d480c.
pos_a = 6, &a = 0x7ffc159d4808.
In main(), x = 5, &x = 0x7f74a34f502c.
In main(), y = 4, &y = 0x7f74a34f5030.
9.2.3 静态持续变量
静态持续变量在整个程序执行期间都存在,但是提供了3种链接性:
- 外部链接性: 声明在代码块外,可以被其他文件共享;
- 内部链接性: 声明在代码块外,并且使用限定符
static
,在声明开始到文件结尾都有效,但是其他文件不可用; - 无链接性: 声明在代码块内,并且用限定符
static
。类似于局部变量,只能在声明的代码块内可用,但是一直保留到程序结束。
9.2.4 静态持续性、外部链接性
1. 单定义规则
每个使用外部变量的文件都必须声明它,但是变量只能定义一次。因此c++提供了2种声明的方法:
- 定义声明:
- 引用声明: 声明前使用关键字
extern
,且不进行初始化。
注意:单定义规则不是说不能有同名的变量,当定义了同名的局部变量,局部变量会替代全局变量。
在局部变量作用内可以使用作用域解析运算符::
来使用变量的全局版本。
9.2.5 静态持续性、内部链接性
使用关键字static
用于作用域为整个文件的变量时,该变量的链接性为内部的。这种变量只能在所属的文件全范围内可用。
- 当定义了一个与外部变量同名的内部静态变量时,内部变量将隐藏外部变量。
9.2.6 静态持续性、无链接性
static
用于作用域为代码块的局部变量,这种变量是无链接性的,即只在代码块内可用,但是一直存在于内存内。
注意:无链接性局部变量只在第一次调用时执行一次初始化。
#include <iostream>//无链接静态变量和局部变量的区别
using namespace std;
void show( int a)
{
static int b = 0;
int c = 0;
b += a;
c += a;
cout << " b = " << b <<", c = " << c <<".\n";
}
int main()
{
int a = 1;
show(a);//b为无链接静态变量只初始化一次,因此b每次调用都执行初始化后面的操作
show(a);
return 0;
}
b = 1, c = 1.
b = 2, c = 1.
9.2.7 说明符和限定符
- 存储说明符
- auto //自动变量
- register //寄存器存储
- static //静态持续性变量
- extern //外部链接变量引用声明
- thread_local //线程存储变量
- mutable //保留cosnt的修改性
struct data
{
char name[5];
mutable int a ;//保留了a得可修改性
}
const data vvv = {"aaaa", 1};
vvv.name = "bbb";//name不可以被修改
Interpreter Error:
vvv.a = 2;//a可以修改
- 限定符
const
const
全局变量的链接性是内部的,所以在头文件中的const常量可用被多个源文件包括而没有冲突。- 在函数或代码块中声明const时,其作用域为代码块。
9.2.8 函数的链接性
- 函数和变量类似,但函数默认情况下为静态外部链接的。
- 可以使用关键字
static
将函数链接性设置为内部的,即只在当前文件内可用,且可以定义同名的外部链接性的函数。
9.2.9 存储方案和动态分配
- 使用
new
运算符初始化
// c++98
int *pi = new int (6); //*pi set to 6;
double * pd = new double (9.9);
//c++11
int *ar = new int [4] {2,4,5,6};
9.3 名称空间
。。。