文章目录
内存模型与命令空间
单独编译
C++也允许鼓励程序员将组件函数放在 独立文件中 。
将 一个程序放在多个文件中 ,将引出新的问题:不能简单的以main()之后的虚线为界,将原来的文件分为两个。问题在于 main()和其他两个函数使用了同一个结构声明 ,因此两个文件都应包含该声明简单的将它们输入进去无疑是自找麻烦,即使正确地复制了结构声明,如果之后要做修改,则必须记住对两组声明都要进行修改。
因此他们提供了#include来处理这种情况,与其将结构声明加入到每个文件中,不如 将其放在头文件中 ,然后再每个源代码文件中包含该头文件,这样要修改结构声明时,只需在头文件中做一次改动即可。
因此可以将函数原型放在头文件中,将原来的程序分成三部分:
- 头文件:包含 结构声明 和 使用这些结构的 函数的原型 。
- 源代码文件:包含 与结构有关 的函数的代码。
- 源代码文件:包含 调用与结构相关 的函数的代码。
如果在头文件中包含一个函数定义,然后将在其他两个文件中包含该头文件,则同一个程序中包含同一个函数的两个定义,除非函数是内联的,否则将会出错,下面列出了头文件中常包含的内容。
- 函数原型
- 使用#define和const定义的符号常量
- 结构声明
- 类声明
- 模板声明
- 内联函数
将结构声明放在头文件中是可以的,因为他们不创建变量,而只是在源代码文件中声明结构变量是告诉编译器如何创建改变量。
模板声明不是将被编译的代码,他们指示编译器如何生成与源代码中的函数调用相匹配的函数定义。
被声明为const的数据和内联函数有特殊的链接属性,因此可以将其放在头文件中,而不会引起问题。
在包含头文件的文件时,如果文件名 包含在尖括号中 ,则C++编译器将 在储存标准头文件的主机系统的文件中查找 ,但如果文件名包含在 双引号中 ,则编译器将 首先查找当前的工作目录或源代码目录,如果没有在,将在标准位置查找 ,因此包自己的头文件时,应使用引号,而不是兼括号。
只需要将源代码文件加入到项目中,而不用加入头文件,这是因为#include指令管理头文件,另外不要使用#include来包含源代码文件,这样将导致多重声明。
在IDE中,不要将头文件加入到项目列表中,也不要在源代码文件中使用#define来包含其他源代码文件。
储存持续性、作用域、连接性
C++使用四种不同的方案来储存数据
- 自动存储持续性:在函数定义中声明的变量是储存持续性为自动的(开始执行其所属的函数或代码块时,被撞见,在执行完函数或代码块时内存被释放)
- 静态存储持续性:在函数定义的变量和使用关键词static定义的变量的存储持续性都是静态的。 在整个程序运行过程中都存在 。
- 线性存储持续性:这能让程序能够将计算放在可并行处理的不同线程中。
- 动态存储持续性,用new运算符分配的内存将一直存在,直到使用delete运算符将其释放或程序为终止。
作用域和连接
作用域:描述名称,在文件的最大可见范围。(例如函数中定义的变量可在该函数中使用,但是不能在其他函数中使用)
链接性:描述了名称如何在不同单元间共享:
- 链接性为 外部的名称 ,可在 文件间共享 。
- 连接性为 内部的名称 , 只能 由一个 文件中的函数共享
- 自动变量的名称 没有连接性 ,因为他们 不能共享 。
C++变量的作用域有多种, 局部的变量 只在 定义它的代码块中 ,可用代码块是由花括号括起来的一系列语句。
作用域为 全局的变量 ,在 定义位置到文件结尾之间 都可用。
在类中声明的成员作用寓于为整体类这个名称空间中,商名的变量的作用域为整个名称空间。
C++函数的作用域可以是整个类或整个命名空间,但是不能是局部的(因为不能在代码块内定义函数,如果函数的作用域是局部,则只对他自己的可见,因此不能被其他函数调用,这样的函数将无法运行。)
自动变量的作用域为局部静态变量的作用域是 全局还是局部 取决于它是如何被定义 的
在 函数原型作用域中 使用的名称只在包含参数列表的括号内可用,在类中声明的成员作用域为整个类在名称空间中声明的全局变量的作用域为整个命名空间。
自动存储连续性
在默认情况下,函数中声明的函数参数和变量的存储连续性为自动,作用域为局部,没有链接性,也就是说如果在main()中声明了一个名为a的变量,并在自定义函数oil()中也声明了一个名为a的变量,则创建了两个相互独立的变量,只在定义它们的函数中才能使用它们。(对oil()中的a执行,任何操作都不会影响main()中的a,反之亦然。)当程序开始执行 ** 这些变量所属的代码块时 ** ,将为其分配内存,当函数结束时这些变量都会消失。
int main()
{
int teledeli = 5; //内部和外部都可见。
{
cout << "hello" << endl;
int weblsight = -2; //只有在内部可见
cout << weblisight; // 内部可见的实例。
cout << teledeli ; //内部可见的实例。
}
cout << teledeli; //外部可见的实例。
}
自动变量只在包含它们的函数或代码块中可见。
1. 自动变量初始化
int w;
int x=4;
bint y=2*x;
2. 自动变量和栈
- 由于自动变量的数目,随函数的开始和结束而增减,因此程序必须运行在运行时对自动变量进行管理。常用方法是流出一段内存并视为栈,以管理变量的增减,之所以称为 ** 栈 ** 是由于新数据被象征性的放在原有数据的上面,当程序使用完后,将从栈中删除。
- 程序使用 ** 两个指针来跟踪栈 ** ,一个指针指向栈底-开始的位置,另一个指着指向栈顶-下一个可用内存单元。(栈是后进先出的即最后加入到栈中的变量首先被弹出)
- 当函数被调用时,其自动变量被加入到栈中,栈顶指针指向下一个可用的内存单元。函数结束时,栈顶指针被重置为函数被调用的值,从而释放新变量使用的内存。
3. 寄存器变量
关键词register最初是由C语言引入的, 他建议编译器使用CPU寄存器来存储自动变量 。
register int count fast; 这可以提高访问变量的速度。
鉴于关键词register只能用于原来就有的自动变量,使用它的唯一原因是指出程序员想使用一个自动变量,这个变量的名称可能与外部变量相同。然而保留关键字register的重要原因是避免使用了关键字的现有代码非法。
静态持续变量
静态持续变量提供了三种连接性, ** 外部连接性,内部连接性和无连接性 ** ,这三种连接性都在整个程序执行期间存在与自动变量相比,他们的寿命更长。
编译器 加分配固定的内存块来储存所有的静态变量 ,这些变量在整个程序执行期间一直存在。如果没有显示的初始化静态变量,编译器将把它们设置为0,在默认情况下静态速度和结构,将每个元素和成员的所有位都设置为0。
传统的K&R C不允许初始化自动速度和结构,但是允许初始化静态速度和结构,ANSI C和C++允许对两种数组和结构进行初始化。
int global = 100 ;
static int one_file = 50 ;
int main ( )
{
}
void funct ( int n )
{
static int count = 0 ;
int llama = 0 ;
}
// globla,one_file,count 这些变量在整个程序执行过程种都存在。
// 即使funct()函数没有执行时,count也留在内存中。
5种变量储存方式
储存描述 | 持续性 | 作用域 | 链接性 | 如何声明 |
---|---|---|---|---|
自动 | 自动 | 代码块 | 无 | 在代码块中 |
寄存器 | 自动 | 代码块 | 无 | 在代码块中使用关键词rehister |
静态无链接性 | 静态 | 代码块 | 无 | 在代码块中使用关键词static |
静态外部链接性 | 静态 | 文件 | 外部 | 不在任何函数内 |
静态内部链接性 | 静态 | 文件 | 内部 | 不在任何函数内使用关键词static |
静态变量的初始化
零初始化和常量表达式初始化被统称为静态初始化;动态初始化意味着变量,将在编译后初始化。
静态持续性、外部链接性
链接性为外部的变量,通常简称为外部变量,它们的存储连续性为静态,作用域为整个文件,外部变量是在函数外部定义的,因此对所有函数而言都是外部的。(在main()函数前面或头文件中定义的,可以在文件中,位于外部变量定义后面的任何函数中使用它,因此外部变量也称 ** 全局变量 ** )
C++有 ** 单定义规则 ** ,该规则指出变量只能有一次定义,为满足这种需求,C++提供了两种变量声明一种是 定义声明或简称为定义 ,他给变量分配储存空间。另一种是 引用声明或简称声明 ,他不给变量分配存储空间,因为他引用已有的变量。引用声明使用关键词并且不进行初始化,否则声明为定义导致分配储存空间。
如果要在多个文件中使用外部变量,只需在一个文件中包含该变量的定义(单定义规则),但是在使用该变量的其他所有文件中,都必须使用关键词extern声明它。
//file01.cpp
int cats = 20 ;
int dogs = 22 ;
int fleas ;
//file02.cpp
extern int cats;
extern int dogs;
extern int fleas;
//file03.cpp
extern int cats;
extern int dogs;
// 这里的所有文件可以使用file01.cpp文件中定义的变量cats,dogs,但是file03.cpp没有重新声明变量fleas,所以他不能访问他。
单定义规则 并非意味着不能有多个变量的名称相同 ,例如在不同函数中声明的同名自动变量时彼此独立的,他们都有自己的地址。
# 包括 <法师>
使用 的名字空间 的性病 ;
外部 双 变暖 ;
int main ( )
{
无效的 更新的 ( 双 dt ) ;
无效的 地方 ( ) ;
return 0;
}
void update(double dt)
{
extern double warming;
warming += dt;
cout << "warming=" << warming << endl;
}
void local()
{
double warming = 0.8;
cout << "局部 warming=" << warming << endl;
cout << "全局 warming=" << ::warming << endl;
}
// update修改了warming,这种修改会在随后该变量时显现出来。
// update使用关键词extern重新声明的变量warming这个关键词的意思是通过这个名称使用在外部定义的变量。
// local函数表明定义与全局变量同名的局部变量后,全局变量将被隐藏,使用局部变量。
// C++比C语言更进一步,它提供作用于解析符号(::),放在变量名前,该运算符表示使用变量的全局版本。
局部变量最有吸引力的是所有函数都能够访问全局变量,但是程序越能避免对数据进行不必要的访问,就越能保证数据的完整性。
外部存储尤其适用于表示常规数据,因为这样可以使关键词 const来阻止数据被修改。
const char* const months[5] = {} ;
第1个const防止字符串被修改;
第2个const确保数组中每一个指针始终指向它最初指向的字符串。
静态持续性,内部链接性
使用static限定符用于作用域为整个文件的变量时,该变量的链接性将为内部的。在多文件程序中,内部链接性和外部链接性之间的差别很有意义,链接性为内部的变量,只能在其所属的文件中使用,但常规外部变量都具有外部链接性,即可以在其他文件中使用。
静态存储持续性,无连接性
链接性分别为内部和外部作用域为整个文件的变量。
无链接性的局部变量:创建是将限定static符用于在代码块中定义的变量,在代码块中使用static时,将导致局部变量的存储持续性为静态的。这意味着,虽然该变量只在该代码块中可用,但他在该代码块儿不处于活动状态时,仍然存在。 因此在两次函数调用之间,静态局部变量的值将保持不变 。
命名空间
C++利用命名空间来解决同名冲突的问题。
-
声明区域:可以在其中进行声明的区域。例如可以在函数外面声明全局变量,对于这种变量其声明区域为其声明所在的文件,对于在函数中声明的变量,其声明区域为其声明所在的代码块。
-
潜在作用域:变量的潜在作用域 从声明点开始到其声明区域的结尾 ,因此潜在作用域比声名区域小,这是由于变量必须定义后才能使用。
变量并非在其潜在作用域内的任何位置都可见,例如它可能被另一个在嵌套声明区域中声明的同名变量隐藏。在函数中声明的局部变量将隐藏在同一个文件中声明的全局变量变量,对程序而言可见的范围被称为作用域。 -
C++关于全局变量和局部变量的规则定义了一种名称,空间层次每个声明区域都可以声明名称这些名称独立于在其声明区域中声明的名称。(在一个函数中声明的局部变量不会在另一个函数中声明的,局部变量发生冲突。)
命名空间的声明
namespace name
{
namespace member
} //此处没用;
namespace companyA
{
void fun ( ) { }
int num ;
}
// 调用
companyA :: fun ( ) ;
companyA :: num = 100 ;
using声明
- 用using指定命名空间之后就不用再调用时总是添加前面的命名空间。
- using声明将特定的名称添加到他所属的声明区间中。例如main()中的using声明using jill::fetch,将fetch添加到main()定义的声明中,完成该声明后,便可以使用fecth名称来代替jill::fetch。