2020年8月13日
第九章 内存模型和名称空间
本章内容概括
单独编译
存储持续性(数据保存在内存中的时间长度)、作用域和链接性(程序的哪一部分可以访问数据)
定位new运算符(动态分配内存)
名称空间(控制访问权的方式)
1.单独编译:
C++允许甚至鼓励程序员将组件函数放在独立的文件中,可以单独编译这些文件,然后将它们链接成可执行的程序,编译器既编译程序,也管理链接器。
头文件中常包含的内容:
函数原型
使用#define或const定义的符号常量
结构声明
类声明
模板声明
内联函数
如果文件名包含在尖括号中,则编译器将在存储标准头文件的主机系统的文件系统中查找;
如果文件名包含在双引号中,则编译器将首先查找当前的工作目录或源代码目录,如果没有找到,将在标准位置查找。
因此在包含自己的头文件时,应使用引号而不是尖括号。
2.存储持续性:
3.作用域:名称在文件的多大范围内可见。
链接性:名称如何在不同单元间共享。
4.自动存储连续性:
int main( )
{
int teledeli = 5;
{
cout<<"Hello\n";
int websight = -2;
cout<<websight<<' '<<teledeli<<endl;
}
cout<<teledeli<<endl;
}
websight的作用域是整个内括号包括区域。
teledeli是整个外括号包括区域。
5.
6.它建议使用CPU内存器来存储自动变量:
这旨在提高访问变量的速度。
register int count_fast;
7.静态持续变量:
静态变量的数目在程序运行期间是不变的,因此程序不需要使用特殊的装置(如栈)来管理它们。编译器将分配固定的内存块来存储所有的静态变量。
如果没有显式地初始化静态变量,编译器将把它设置为0。在默认情况下,静态数组和结构将每个元素或成员地所有位都设置为0。
8.创建链接性为外部的静态持续变量,必须在代码块的外面声明它
创建链接性为内部的静态持续变量,必须在代码块的外面声明它,并使用static限定符
创建没有链接性的静态持续变量,必须在代码块内声明它,并使用static限定符
9.引用声明使用关键字extern,且不进行初始化,否则声明为定义,导致分配存储空间。
如果在多个文件中使用外部变量,只需在一个文件中包含该变量的定义(单定义规则),但在使用该变量的其他所有文件中,都必须用关键字extern声明它。
10.作用域解析运算符(: :)放在变量名之前时,该运算符表示使用变量的全局版本。
11.程序越能避免对数据进行不必要的访问,就越能保持数据的完整性。
全局变量可以让多个函数可以使用同一数据块。外部存储尤其适用于表示常量数据,使用const来防止数据被修改。
12.使用外部变量在多文件程序的不同部分之间共享数据;
使用链接性为内部的静态变量在同一个文件的多个函数之间共享数据。(名称空间提供了另外一种共享数据的方法)
如果将作用域为整个文件的变量变为静态的,就不必担心其名称与其他文件中的作用域为整个文件的变量发生冲突。
13.
14.cv-限定符
const/volatile
关键字volatile表明,即使程序代码没有对内存单元进行修改,其值也可能发生变化。
在这种情况下,硬件可能修改其中的内容。或者两个程序之间会相互影响,共享数据。该关键字改善了编译器的优化能力,将变量声明为volatile,在程序多次使用某个变量的值时,则编译器不会进行将数值缓存到寄存器的优化。
15.mutable
即使结构(或类)变量为const,其某个成员也可以被修改。
相当于一种通行证。
16.const全局变量的 链接性为内部的,在C++看来,全局const定义,就像使用了static说明符。因此,需要为某个文件使用一组定义,而其他文件使用另一组声明,且可以在所有文件中使用相同的说明。
内部链接性还意味着,每个文件都有自己的一组常量,而不是所有文件共享一组常量。每个定义都是其所属文件私有的,因此能够将常量定义放在头文件中。
17.函数和链接性:
在默认情况下,函数的链接性为外部的,即可以在文件间共享。还可以使用关键字static将函数的链接性设置为内部的,使之只能在一个文件中使用。必须同时在原型和函数定义中使用该关键字。
在定义静态的文件中,静态函数将覆盖外部定义,因此即使在外部定义了同名的函数,该文件仍将使用静态函数。
单定义规则也适用于非内敛函数,因此对于每个非内联函数,程序只能包含一个定义。对于链接性为外部的函数来说,意味在多文件程序中,只能有一个文件(可能是库文件)包含该函数的定义,但使用该函数的每个文件都应该包含其函数原型。
内联函数则不受约束。
18.查找函数
如果在程序文件中没有找到,编译器将在库中搜索。这意味着如果定义了一个与库函数同名的函数,编译器将使用程序员定义的版本,而不是库函数。
19.语言链接性:
C++语言链接:C++编译器执行名称矫正或名称修饰,为重载函数生成不同的符号名称。
20.在C++程序中使用C库中预编译的函数,为了解决符号名称格式不匹配的问题,可以使用函数原型来指定要使用的约定。
extern "C" void spiff(int);
21.存储方案和动态分配
动态内存由运算符new和delete控制,因此可以在一个函数中分配动态内存,而在另一个函数中将其释放。动态内存不是LIFO,其分配和释放顺序要取决于new和delete在何时何地何种方式被利用。
存储方案概念不适用于动态内存,但适用于跟踪动态内存的自动和静态指针变量。
22.在程序结束时,由new分配的内存通常都被释放,在某些情况下,请求大型内存块将导致该代码块在程序结束不会被自动释放。最佳做法是使用delete.
23.使用列表对动态分配的变量初始化。
24.
void * operator new(std::size_t);
void * operator new[ ](std::size_t);
void operator delete(void *);
void operator delete[ ](void*);
std::size_t是一个typedef
int*pi=new int int*pi=new(sizeof(int));
int*pa=new int[40] int*pa=new(40*sizeof(int));
delete pi delete (pi)
25.new负责在堆中找到一个足以能够满足要求的内存块。定位new运算符让您能够指定要使用的位置。程序员可能使用这种特性来设置其内存管理规程、处理需要通过特定地址进行访问的硬件或在特定位置创建对象。
使用定位new特性,首先需要包含头文件new,它提供了这种版本的new运算符的原型.
26.使用定位运算符时,会出现类型不匹配的情况,void*可进行强制类型转换。
常规new将数组放在很远的地方,位于动态管理的堆中。
27.delete只能用于释放堆内存。
定位new的静态内存在它的管辖区域之外。
28.定位new运算符的工作原理:返回传递给它的地址,并将其强制转换为void*,以便能够赋给任何指针类型。
29.作用域:变量对程序而言可见的范围。
潜在作用域:从声明点开始,到其声明区域的结尾。
30.using声明和using编译指令
using声明将特定的名称添加到它所属的声明区域中。完成声明后,可用name代替domine::name.
在函数的外面使用using声明时,将把名称添加到全局名称空间中。
using声明使一个名称可用,而using编译指令使所有的名称都可用。
会增加名称冲突的可能性,可能会导致二义性。
31.使用using声明比使用using编译指令更加安全,只导入指定的名称。如果该名称与局部名称发生冲突,编译器将发出指示。
using编译指令导入所有名称,包括可能并不需要的名称。如果和局部名称发生冲突,则局部名称将覆盖名称空间版本,而编译器不会发出警告。
名称空间的开放性意味着名称空间的名称可能分布在多个地方。
32.using namespace std;
int x;
std::cin>>x;
std::cout<<x<<std::endl;
using std::cin;
using std::cout;
using std::endl;
int x;
cin>>x;
cout<<x<<endl;
最常用的应该是第三种把。
33.名称空间也可以套娃。
也可以在名称空间中使用using编译指令和using声明。
::表示从属关系。
34.using编译指令是可传递的
如果
namespace myth
{
using namespace elements;
}
Using namespace myth;
等效于
Using namespace myth;
Using namespace elements;
35.给名称空间创建别名,此时的namespace 类似于一种数据类型,可以把它想想成一种类似于桥梁的结构。
namespace my_very_favorite_things;
namespace mvft=my_very_favorite_things;
36.未命名的名称空间:
名称空间没有名称,不能显式地使用using方法,即不能再未命名名称空间所属文件之外地其他文件内使用该名称空间中地名称,成为了链接性为内部的静态变量的替代品。
static int counts
等效于
namespace
{
int counts;
}
37.名称空间使用时的指导原则
38.使用名称空间的主旨是简化大型变成项目的管理工作。
老式头文件(iostream.h)没有使用名称空间,
但新头文件iostream使用了std名称空间。