《C++Primer Plus》读书笔记 第9章 内存模型和名称空间

第9章 内存模型和名称空间

1.include语句的使用

在使用include语句时,如果文件名包含在尖括号中,则cpp编译器将在存储标准头文件的主机系统的文件系统中查找;但如果文件名包含在双引号中,则编译器将首先查找当前的工作目录或源代码目录(或其他目录,这取决于编译器)。如果没有在那里找到头文件,则将在标准位置查找。

2.头文件管理

在同一个头文件中只能将同一个头文件包含一次。通常利用#ifndef和#endif语句来完成:

#ifndef XXX_H_
#define XXX_H_
//place include file contents here
#endif

这样,如果在另外一个文件中已经定义了XXX_H_这个常量,则说明这个头文件已经被包含了。XXX通常命名为头文件名。

3.四种数据存储方案

自动存储持续性:在函数定义中声明的变量(包括函数参数)的存储持续性为自动的。它们在程序开始执行其所属函数或代码块时被创建,在执行完函数或代码块时,它们使用的内存被释放。

静态存储持续性:在函数定义外定义的变量和使用关键字static定义的变量的存储持续性都为静态。它们在整个程序运行过程中都存在。

线程存储持续性:如果变量是使用关键字thread_local声明的,则其生命周期与所属的线程一样长。

动态存储持续性:用new运算符分配的内存将一直存在,直到使用运算符将其释放或程序结束为止。

4.五种变量存储方式

存储描述持续性作用域链接性声明方式
自动自动代码块在代码块中
寄存器自动代码块在代码块中,使用关键字register
静态,无链接性静态代码块在代码块中,使用关键字static
静态,外部链接性静态文件外部不在任何函数内
静态,内部链接性静态文件内部不在任何函数内,使用关键字static

5.静态变量的初始化方式

除了默认的零初始化之外,还可以对静态变量进行常量表达式初始化动态初始化。零初始化和常量表达式初始化被统称为静态初始化,这意味着在编译器处理文件(翻译单元)时初始化变量。动态初始化意味着变量将在编译后初始化。

首先,所有静态变量都被零初始化,而不管程序员是否显式地初始化了它。接下来,如果使用常量表达式初始化了变量,且编译器仅根据文件内容(包括被包含的头文件就可计算表达式,编译器将执行常量表达式初始化。必要时,编译器将执行简单计算。如果没有足够的信息,变量将被动态初始化。

#include<cmath>
int x;      //零初始化
int y = 5;    //常量表达式初始化
long z = 13 *13;    //常量表达式初始化
const double pi = 4.0 * atan(1.0);  //动态初始化

在上面的代码中,首先x,y,z和pi被零初始化。然后,编译器计算常量表达式,并将y和z初始化为5和169。但要初始化pi,必须调用函数atan(),这需要等到该函数被链接且程序执行时。

6.单定义规则

C++提供了两种变量声明。一种是定义声明或简称为定义,它给变量分配存储空间;另一种是引用声明或简称声明,它不给变量分配存储空间,因为它引用已有的变量。

引用声明使用关键字extern,且不进行初始化。如果进行了初始化,声明就为定义,导致分配存储空间。

如果要在多个文件中使用外部变量,只需在一个文件中包含该变量的定义(单定义规则),但在使用该变量的其他所有文件中,都必须使用关键字extern声明它。

7.作用域解析符

作用域解析运算符(::)放在变量名前面时,该运算符表示使用变量的全局版本。

8.存储说明符

  • auto(在C++11中不再是说明符)
  • rigister
  • static
  • extern
  • thread_local
  • mutable

关键字register指出变量为寄存器变量。

关键字thread_local指出变量的持续性与其所属线程的持续性相同。

关键字mutable指出即使结构(或类)变量为const,其某个成员也可以被修改。

9.const变量的链接性

const全局变量的链接性为内部的。也就是说,在C++看来,全局const定义就像使用了static说明符一样。

如果出于某种原因,程序员希望某个常量的链接性为外部的,则可以使用extern来覆盖默认的内部链接性:

extern const int states = 50;

在这种情况下,必须在所有使用该常量的文件中使用extern关键字来声明它。

10.函数的链接性

所有函数的存储持续性都自动为静态的,即在整个程序执行期间都一直存在。在默认情况下,函数的链接性为外部的,即可以在文件间共享。实际上,可以在函数原型中使用关键字extern来指出函数是在另一个文件中定义的。还可以使用关键字static将函数的链接性设置为内部的,使之只能在一个文件中使用。必须同时在原型和函数定义中使用static关键字。

11.new运算符的使用

通常,编译器使用三块独立的内存:一块用于静态变量,一块用于自动变量,另一块用于动态存储。其中,动态存储的内存由运算符newdelete控制,而不是由作用域和链接性规则控制。

如果要为内置的标量类型(如int或double)分配存储空间并初始化,可在类型名后面加上初始值,并将其用括号括起:

int *pi = new int (6);
double *pd = new double (99.99);

然而,要初始化常规结构或数组,需要使用大括号的列表初始化,这要求编译器支持C++11:

struct where {double x; double y; double z};
where *one = new where{2.5, 5.3, 7.2};
int *ar = new int [4] {2, 4, 6, 7};

在C++11中,还可以将列表初始化用于单值变量:

int *pin = new int {6};
double *pdo = new double {99.99};

通常,new负责在堆中找到一个足以能够满足要求的内存块。new运算符还有另一种变体,被称为定位new运算符,它让你能够制定要使用的地址。要使用定位new特性,首先需要包含头文件new。

char buffer[20];
int *p = new (buffer) int[20];

定位new运算符使用传递给它的地址,它不跟踪哪些内存单元已被使用,也不查找未使用的内存块。

12.声明区域和潜在区域

声明区域是可以在其中进行声明的区域。潜在作用域是变量从声明点开始,到其声明区域的结尾。

13.名称空间的创建

名称空间的创建语句:

namespace Jack
{
	double pail;
	void fetch();
	int pal;
	struct well{...};
}

可以给名称空间创建别名:

namespace J = Jack;

也可以创建通过省略名称空间的名称来创建未命名的名称空间。

名称空间是开放的,即可以在任何地方(甚至另一个文件中)把名称加入到已有的名称空间中。例如,下面这条语句将名称goose加入到Jack中已有的名称列表中:

namespace Jack
{
	char * goose(const char *);
}

可以用作用域解析运算符”::”来使用名称空间中的名称:

Jack::pail=12.34;

未被名称空间装饰的名称称为未限定的名称;包含名称空间的名称称为限定的名称

14.using声明和using编译指令

using声明由被限定的名称和它前面的关键字using组成:

using Jack::pail;

using声明将特定的名称添加到它所属的声明区域中。完成该声明后就可以直接使用pail。

using编译指令由名称空间名和它前面的关键字using namespace组成,它使名称空间中的所有名称都可用,而不需要使用作用域解析运算符:

using namespace Jack;

如果某个名称已经在函数中声明了,则不能using声明导入相同的名称。如果使用using编译指令导入一个已经在函数中声明的名称,则局部名称将隐藏名称空间名,就像隐藏同名的全局变量一样。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值