Chapter9:内存模型和名称空间
- 1. C++源文件组织策略
- 我们先看下面程序清单: coordin.h
- main.cpp
- Coordin.cpp
- 2. 内存存储方案
- 2.1 自动存储持续性
- 2.2 静态存储持续变量
- 2.3 存储方案和动态分配
本章节从三个大方面做总结
- C++多个源文件组织方式
- C++存储方案
- C++名称空间
💚 在源文件组织方式这一章:我会分析总结C++关于多个源文件组织策略,在第二章:我会从C++存储持续性,作用域和链接性来分析存储模型,在第三章节:我会分析名称空间标识符使用,其中重点是第二章节。
1. C++源文件组织策略
我们先看下面程序清单:
coordin.h
#ifndef COORDIN_H_
#define COORDIN_H_
struct polar
{
double distance; // distance from origin
double angle; // direction from origin
};
struct rect
{
double x; // horizontal distance from origin
double y; // vertical distance from oeigin
};
// prototypes
polar rect_to_polar(rect xypos);
void show_polar(polar dapos);
#endif
main.cpp
#include<iostream>
#include "coordin.h" // structure templates function
using namespace std;
int main()
{
rect rplace;
polar pplace;
cout<< "Enter the X and Y values: ";
while(cin >> rplace.x >> rplace.y)
{
pplace = rect_to_polar(rplace);
show_polar(pplace);
cout << "Next two numbers(q to quit):";
}
cout << "Bye ! \n";
return 0;
}
Coordin.cpp
#include<iostream>
#include<cmath>
#include "coordin.h"
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 dapos)
{
using namespace std;
const double Rad_to_deg = 57.29577951;
cout << "distance = " << dapos.distance;
cout<< ", angle = " << dapos.angle * Rad_to_deg;
cout << " degrees\n";
}
💚 下面我们通过 “ g++ ” 编译指令来编译链接 main.cpp 和 coordin.cpp 两个源文件。
注意点:
- 在同一个源文件cpp中 只能将同一个头文件包含一次,所以 coordin.h 使用: #ifndef 和 # endif 语句 ,如果没有使用预处理器编译指令 #define 定义的名称 COORDIN_H 时,才处理 #ifndef 和 # endif 之间的语句。
- 虽然我们讨论的是根据文件进行单独编译,但是为了保持通用性,C++标准使用了术语翻译单元 (translation unit),而不是文件。
2. 内存存储方案
首先我们介绍变量在内存的时间,即生命周期。
有四种不同方案来存储数据。
- 自动存储持续性:在函数定义中声明的变量(包括函数参数)的存储持续性为自动的。他们在程序开始执行其所属函数或代码块中被创建,在执行完函数或代码块时,他们使用的内存被释放。通常意义上,我们把这称之为:栈变量。C++ 有两种存储持续性为自动的变量
- 静态存储持续性:在函数定义外定义的变量和 关键字 static 定义的变量的存储持续性都称为静态,他们在整个程序运行过程中都存在。C++有三种存储持续性为静态的变量
- 线程存储持续性:当前,多核处理器很常见,这些CPU可以处理多个任务,这让程序能够将计算放在可并行处理的不同线程中,如果变量时使用关键字 thread_Local 声明的,则其生命周期与所属线程一样长。
- 动态存储持续性:用 new 运算符分配的内存将一直存在,直到使用 delete运算符将其释放或程序结束为止,这种内存的存储持续性为动态,有时被称为 自由存储或堆。
💚💚💚
下面介绍变量的作用域(可被程序使用)以及链接性的细节。
作用域(ascope 😃
描述了名称在文件(翻译单元)的多大范围内可见: 例如 :函数中定义的变量可在该函数中使用,但不能在其他函数中使用。
链接性(linkage):
描述了名称如何在不同单元间共享,链接性为外部的名称可在文件间共享,链接性为内部的名称只能由一个文件中函数共享,例如:自动变量的名称没有链接性,因为它们不能共享。
💚
- 自动变量的作用域为局部。
- 静态变量的作用域为全局还是局部:这取决于变量该如何定义。如:在函数原型作用域定义,作用域为局部。 在类中声明的成员的作用域为整个类,在名称空间中声明的变量作用域为 整个名称空间。
2.1 自动存储持续性
默认情况下,函数中声明的函数参数和变量的存储持续性为自动,作用域为局部,没有链接性。
💚 自动变量的初始化
int w; // value of w is 模糊
int x = 5; // initialized with a numeric literal
int big = INT_MAX-1; // initialized with a constant expression
int y = 2*x; // use previously determined value of x
int z = 3*w; // use new value of w
💚 自动变量和栈
例子:定义函数 fib(int real, long tell) ,我们来看下 函数栈运行流程。
注意:程序中使用两个指针来跟踪栈,一个指针指向栈底:栈的开始位置,一个指针指向栈顶:下一个存储单元。
2.2 静态存储持续变量
C++ 为静态存储持续性变量提供了3种 链接性:
- 外部链接性(可在其他文件中访问)
- 内部链接性(只能在当前文件中访问)
- 无链接性(只能在当前函数或代码块中访问)
这三种链接性都在整个 程序中存在,与自动变量相比,它们的寿命更长。
🧡重点:由于静态变量的数目在程序运行期间是不变的,因此程序不需要使用特殊装置(如栈)来管理他们,编译器将分配固定的内存块来存储所有的静态变量,这些变量在整个程序执行期间一直存储。
下面,我们有一个图来说明,如何创建上述三种变量。
🧡 静态变量的初始化
除默认的零初始化外,还可对静态变量进行常量表达式初始化和动态初始化。
零初始化:意味着将变量设置为零,对于标量类型,零将被强制转换为合适的类型。 如: 空指针用0表示,结构成员被零初始化,填充位将都被设置为零。
常量初始化:变量设置为常量
🧡零初始化和常量初始化决定因素
零初始化和常量初始化都被称为静态初始化,首先,所有静态初始化都被零初始化,不管程序员是否显示初始化了它,接下来,如果使用常量初始化,编译器将指针常量表达式。
#include<cmath>
int x; // zero-initialization
int y = 5; // constant ant-expression initialization
long z = 13*13; // constant ant-expression initialization
int enough = 2*sizeof(long); // constant expression initialization
// 因为:pi 的计算需要链接 函数 atan()且被执行时,才能计算出来,它被称为动态初始化
const double pi = 4.0*atan(1.0);// dynamic initialization
💚 💚静态持续性,外部链接性
链接性为外部的变量通常简称为 外部变量,它们的存储持续性为静态(整个程序生命周期),作用域为整个文件。
这里有一个概念:单定义规则
一方面,在每个使用外部变量的文件中,都必须声明它,另一方面,C++中有 “单定义规则(one definition Rule ODR)”,该规则指出,变量只能定义一次,为了满足这种需求,C++提供了两种变量声明:
- 定义声明(defining declaration):或简称定义(declaration) 它给变量分配存储空间,
- 引用声明(referencing declaration ):或简称声明declaration 它不给变量分配存储空间,因为它引用已有变量
- 引用声明使用关键字 extern, 且不进行初始化,否则,声明即为定义,导致分配存储空间。
double up; // definition, up is 0
extern int blem; // blem defined else where
extern char gr = 'z'; // definition because initialized
下面例子说明了,如果要在多个文件中使用外部变量,只需要在一个文件中定义该变量,且在使用该变量的文件中,必须使用关键字 extern。
💚 💚静态持续性,内部链接性
将 static限定符用于作用域为整个文件的变量时,该变量的链接性为内部,存储持续为静态(整个程序生命周期)
💚💚静态存储持续性,无链接性
这种变量是这样创建的:将static 限定符用于代码块中定义的变量,这将会使局部变量的存储持续性为静态(扩展了变量生命周期),但它无链接性,作用域仅限于代码块。
🧡 初始化:程序只在启动时进行一次初始化,以后再调用函数,并不会像自动变量再次初始化
2.3 存储方案和动态分配
前面我们谈了C++变量静态存储和自动存储模型,下面我们来讲讲C++运算符 new(或者C函数 malloc()) 分配的内存,这被称为动态内存。
动态内存由运算符 new/delete 控制,不是由作用域和链接性规则控制。