C++ primer plus读书笔记——第9章 内存模型和名称空间

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

1. 头文件常包含的内容:

函数原型。

使用#define或const定义的符号常量。

结构声明。

类声明。

模板声明。

内联函数。

2. 如果文件名被包含在尖括号中,则C++编译器将在存储标准头文件的主机系统的文件系统中查找。但如果头文件名包含在双引号中,则编译器将首先查找当前的工作目录或源代码目录(或其他目录,这取决于编译器)。如果没有在那里找到头文件,则将在标准位置中查找。因此在包含自己的头文件时,应使用引号而不是尖括号。

3. 链接程序将目标文件代码、库代码和启动代码合并,生成可执行文件。

4. 在同一个文件中,只能将同一个头文件包含一次。因此,通常使用:

  #ifndef COORDIN_H_

  #define COORDIN_H_

  #endif

这种防护方案并不能阻止编译器将文件包含两次,而只是让它忽略第一次包含之外的所有内容。

5. C++标准运行每个编译器设计人员以他认为合适的方式来实现名称修饰,因此由不同编译器创建的目标代码文件很可能无法正确地链接。也就是说,两个编译器将为同一个函数生成不同的修饰名称。

6. C语言和C++ 11以前的版本中,将auto关键字用于默认为自动的变量,因此程序员几乎不使用它。在C++11中,这种用法不再合法。C++11中auto关键字为自动类型推断。制定标准的人不愿意引入新关键字,因为这样做可能导致该关键字用于其他目的的代码非法。 考虑到auto的老用法很少使用,因此赋予其新含义比引入新关键字是更好的选择。

7. 关键字register最初是由C语言引入的,它建议编译器使用CPU寄存器来存储该变量。在C++11中,这种作用失去了,关键字register只是显示地指出该变量是自动的,这与auto以前的用途完全相同。然而,保留register的重要原因是,避免使用了该关键字的现有代码非法。

8. 外部链接性(可在其他文件中访问)

  内部链接性(只能在当前文件中访问)

  无链接性(只能在当前函数或代码块中访问)

创建链接性为外部的静态变量:在代码块的外面声明它;

创建链接性为内部的静态变量:在代码块的外面声明它,并使用static关键字;

创建无链接性的静态变量:必须在代码块内声明它,并使用static关键字。

int global = 1000;//外部链接性

static int one_file = 50;//内部链接性

void funct1(int n)

{

   static int count = 0;//无链接性

}

9. 所有的静态变量如果未被初始化,所有都被设置为0。静态变量的初始化方式有零初始化(默认),常量初始化,动态初始化。

10. 定义声明(定义),它给变量分配存储空间。

  引用声明(声明),它不给变量分配存储空间,因为它引用已有变量。

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

  double up;//定义声明

  extern int blem;//引用声明

  extern char gr = ‘z’;//定义声明,因为初始化

11. 如果在函数中声明了一个与外部变量同名的变量,要引用外部变量时,需要在该变量名称前加上::作用域解析运算符,表示使用该变量的全局版本。仅适用于C++。

12. P314如果在一个文件中定义了一个常规外部变量,在另一个文件中定义了一个同名的常规外部变量,这种做法属于重复定义,将失败。

如果在一个文件中定义了一个静态外部变量,在另一个文件中定义一个同名的常规外部变量,则在该文件中,静态变量将隐藏常规外部变量。

13. 如果初始化了静态局部变量,则程序只在启动时进行一次初始化。以后再调用该函数时,将不会像自动变量那样再次被初始化。

14. volatile防止编译器进行优化。

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

struct data

{

   char name[30];

   mutable int accesses;

};

const data veep = {“Claybourne Clodde”, 0};

strcpy(veep.name, “Joye Joux”);//不允许

veep.accesses++;                  //允许

16. 在默认情况下,全局变量的链接性为外部的,但const全局变量的链接性为内部的(在C++中,而不是C语言)。也就是说,在C++看来,全局const定义就像使用了static说明符一样。这就是能够将常量定义放在头文件中的原因。

17. 如果由于某种原因,程序员希望某个常量的链接性为外部的,则可以使用extern关键字来覆盖默认的内部链接性:extern const int states = 50;

18. 在默认情况下,函数的链接性为外部的,既可以在文件间共享。实际上,可以在函数原型中使用关键字extern来指出函数是在另一个文件中定义的,不过这是可选的(要让程序在另一个文件中查找函数,该文件必须作为程序的组成部分被编译,或者是由链接程序搜索的库文件)。可以使用关键字static将函数的链接性设置为内部的,使之只能在一个文件中使用。必须同时在原型和函数定义中使用该关键字:

static int private(double x);

static int private(double x)

{

}

和变量一样,在定义静态函数的文件中,静态函数将覆盖外部定义,因此即使在外部定义了同名的函数,该文件仍将使用静态函数。

19. 单定义规则除了适用于变量,也适用于非内联函数。这意味着在多文件程序中,只能有一个文件包含该函数的定义,但使用该函数的每个文件都必须包含其函数原型。内联函数不受这项规则的约束,这允许程序员能够将内联函数的定义放在头文件中。这样,包含了头文件的每个文件都有内联函数的定义。

20. 假设在程序的某个文件中调用一个函数,C++将到哪里去寻找该函数的定义呢?如果该文件中的函数原型指出该函数是静态的,则编译器将只在该文件中查找函数定义;否则,编译器(包括链接程序)将在所有文件中查找。如果在程序文件中找不到,编译器将在库中搜索。这意味着,如果定义了一个与库函数同名的函数,编译器将使用程序员定义的版本,而不是库函数。

21. 链接程序要求每个不同的函数都有不同的符号名。在C语言中,一个名词只对应一个函数,因此这很容易实现。为满足内部需要,C语言编译器可能将spiff这样的函数名翻译为_spiff。这种方法称为C语言链接性(C language linkage)。但在C++中,同一个名称可能对应多个函数,必须将这些函数翻译为不同的符号名称。因此,C++编译器执行名称纠正或名称修饰,为重载函数生成不同的符号名称。例如,spiff(int)转换为—_spiff_i,而将spiff(double, double)转换为_spiff_d_d。这种方法称为C++语言的链接性(C++ language linkage)。

如果要在C++程序中使用C语言预编译的函数,将出现什么情况呢?例如,假设有如下代码:spiff(22);它在C库文件中的符号名称为_spiff,但对于我们的C++链接程序来说,C++查询约定是查找符号民称_spiff_i。为解决这样的问题,可以用函数原型来指出要使用何种约定:

extern “C” void spiff(int);//使用C语言链接性

extern void spoff(int);//使用C++语言的链接性

extern “C++” void spaff(int);//使用C++语言的链接性

22. new 运算符

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

int *pi = new int(6);

要初始化常规结构或数组,需要使用大括号的列表初始化,这要求编译器支持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, 7, 6};

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

int *pin = new int {6};

23. new失败时

  在最初的10年中,C++让new失败时返回空指针,但现在将引发std::bad_alloc异常。

24. P321定位new运算符

  定位new运算符用来将信息放在特定的硬件地址中。delete只能用来释放常规new分配的内存块。

25. 名称空间可以是全局的,也可以位于另一个名称空间之中,但不能位于代码块中。因此,默认情况下,在名称空间中声明的名称的链接性为外部的(除非它引用了常量)。

26. C++提供了两种机制(using声明和using编译指令)来简化对名称空间中名称的使用。using声明使特定的标识符可用,using编译指令使整个名称空间可用。

27. using声明将特定的名称添加到它所属的声明区域中。在函数的外面使用using声明时,将把名称添加到全局名称空间中;在函数的里面使用using声明时,将名称添加到局部声明区域中(例如如果添加的名称是变量,将和其他局部变量一样)。

28. 有关using声明和using编译指令,需要记住的一点是,它们增加了名称冲突的可能性。

例如,在代码中使用作用域解析运算符,则不会存在二义性:

jack::pal = 3;

jill::pal = 10;

然而,如果使用using声明,情况将发生变化:

using jack::pal;

using jill::pal;

pal = 4;

编译器不允许同时使用上述两个using声明,因为这将导致二义性。

29. 一般来说,使用using声明比使用using编译指令更安全,这是由于它只导入指定的名称。如果该名称与局部变量冲突,编译器将发出指示,编译不通过。using编译指令导入所有的名称,包括可能并不需要的名称。如果与局部名称冲突,则局部名称将覆盖名称空间版本,而编译器不会发出任何警告。另外,名称空间的开放性意味着名称空间可能分散在多个地方,这使得难以准确知道添加了哪些名称。

30. 可以在名称空间中嵌套名称空间,也可以在名称空间中使用using编译指令和using声明。

31. using编译指令是可传递的。P330

32. 可以给名称空间创建别名。

  namespace my_very_favorite_things{…};

  namespace mvft = my_very_favorite_things;

  可以使用这种技术来简化对名称空间的使用:

  namespace MEF = myth::elements::fire;

  using MEF::flame;

33. 可以通过省略名称空间的名称来创建未命名的名称空间:

  namespace

      int ice;

      int bandycoot;

  这就像后面跟着using编译指令一样,也就是说,在该名称空间中声明的名称的潜在作用域为:从声明点到该声明区域末尾。从这个方面看,它们与全局变量相似。由于该名称空间没有名称,不能在所属文件之外的其它文件中使用该名称空间中的名称。这就提供了链接性为内部的静态变量的替代品。

34. 使用using声明导入函数名称时,由于只给出函数的名称,因此,如果一个函数被重载,则一个using声明将导入所有的版本。

posted on 2016-01-06 18:09  lakeone  阅读(919)  评论(0编辑  收藏  举报

导航