• 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声明将导入所有的版本。

  • 相关阅读:
    现代软件工程 第一章 概论 第4题——邓琨
    现代软件工程 第一章 概论 第9题——邓琨
    现代软件工程 第一章 概论 第7题——张星星
    现代软件工程 第一章 概论 第5题——韩婧
    hdu 5821 Ball 贪心(多校)
    hdu 1074 Doing Homework 状压dp
    hdu 1074 Doing Homework 状压dp
    hdu 1069 Monkey and Banana LIS变形
    最长上升子序列的初步学习
    hdu 1024 Max Sum Plus Plus(m段最大子列和)
  • 原文地址:https://www.cnblogs.com/lakeone/p/5106546.html
Copyright © 2020-2023  润新知