C++回炉之_C++PrimerPlus_第九章 内存模型名称空间

头文件

  • 包含内容
    • 使用#define或const定义的符号常量
    • 函数原型
    • 结构体声明
    • 类声明
    • 模板声明
    • 内联函数
  • 头文件的作用
    • 对包含头文件的源代码文件(.cpp)进行单独编译时,预处理器将其与源文件合并
    • 从而创建临时文件(.cpp)
  • 使用

    • 系统头文件用#include
    • 用户头文件用#inllude "XXX.h"
    • 使用条件编译防止多次包含头文件

      
      #ifndef XXX_H
      
      
      #define XXX_H
      
      ...
      
      #endif
      
      
      #pragma once
      
      ...

简述

  • 存储持续性
    • 表示变量在内存中存在的时间
    • 自动存储持续性 – 在执行其属函数或代码块时创建,执行完后其内存被释放
      • 在函数中声明的变量 – 包括参数
      • 在代码块中被声明的变量 – 局部变量
    • 静态存储持续性 – 在程序整个运行过程中都存在
      • 在函数和代码块外定义的变量 – 全局变量
      • 在函数内使用static定义的变量
    • 线程存储持续性(C++11) – 生存周期同所属线程一样长
      • 使用local_thread声明
    • 动态存储持续性 – 直到使用 delete 释放其内存 或 程序运行结束
      • 使用 new 分配内存的变量

  • 作用域
    • 描述名称在文件(翻译单元)的可见范围
    • 局部作用域 – 只在定义它的代码块内可用
      • 自动变量
    • 全局作用域 – 在定义位置到文件结尾都可用
      • 静态变量
    • 函数原型作用域 – 在函数原型作用域中使用的名称只在包含参数表的括号内可用 – 可省略
    • 类作用域 – 在定义它的类中可用
    • 名称空间作用域 – 在定义它的名称空间中可用 – 全局作用域为其一特例

  • 链接性
    • 描述名称在不同单元之间共享的方式
    • 外部链接性 – 可在文件中共享
    • 内部链接性 – 只能由其所在文件中的函数共享
    • 无链接性 – 不能共享 – 如自动变量

自动变量

  • 自动变量一般都是 作为局部的变量 且没有链接性
  • auto原用于显式地指出变量为自动存储 – C++11已经取消此用法
  • 局部变量可隐藏外部的变量(或外层的局部变量)
    • 全局变量若不想被隐藏,可使用域解析符(::)
  • 可使用任何在声明时已知的表达式来初始化自动变量
  • 自动变更使用栈来简化实现
  • 寄存器变量
    • 使用 register 关键字声明
    • 它寻底编译器使用寄存器来存储自动变量 – 旨在提高速度 – 目前已经淘汰
    • 目前已经淘汰,register 关键字只显式说明变量是自动的

静态持续变量的链接性

  • 外部链接性
    • 直接在代码块外声明即可
    • 有两种声明方式
      • 定义声明 – 会给变量分配内存 – 直接声明
      • 引用声明 – 不给变量分配内存(引用已有变量 – 其它文件中的)
        • 使用 extern 关键字 且 不初始化 – 若初始化了,则还是定义声明
      • 一般只在一个文件中使用定义声明,而在其他文件中使用引用声明

  • 内部链接性
    • 直接在代码块外声明, 并在前面加上 static 关键字即可
      • 此时 static 关键字表示为变量的内部链接性 (变量已经是静态的了)
    • 名称相同时,内部链接性的静态变量将隐藏常规的外部变量

  • 无链接性
    • 在代码块内声明,并加个 static 关键字
      • 此时 static 关键字表示为变量的存储持续性为静态 (变量已经是无链接性的了)
      • 这又叫做 关键字重载

说明符和限定符

  • c-v限定符

    • const – 不能修改的内存

      • const 对全局变量的链接性有影响 – const全局变量的链接性为内部的
      • 这也是const变量能写在头文件而没有多重定义的原因
      • 若想使用链接性为外部的const变量,则可以再在前面加extern进行定义,但其他文件使用此变量必须也要用extern关键字进行声明

        extern const int maxn = 100;
    • volatile – 易变的
      • 即使程序代码没有对内存单元进行修改,其值也可能发生变化(可能受硬件影响)
      • 如果不使用volatile变量,在再次访问之间编译器可能会认为值没有变化,从而进行优化(如将值放在寄存器中缓存起来)
      • 使用volatile变量相当于告诉编译器,不要进行这种优化

  • 存储说明符

    • auto – C++11后不再是说明符
    • register – C++11后表示为显式说明为自动变量
    • static – 内部链接 静态
    • extern – 引用声明
    • thread_local – (C++11) 线程的静态变量
    • mutable

      • 可用它来指出,即使结构体(或类)变量为const, 其某个成员也是可以被修改的
      struct node {
          int x;
          mutable int y;
      };
      const node n { 1, 2};
      n.x = 3;                // 错误, 不可修改
      n.y = 4;                // 正确, y为mutable变量

函数的链接性

  • 函数默认为外部静态的 即默认是extern的
  • 可使用 static 关键字将其链接性设置为内部的
    • 必须同时在原型和定义中使用static
    • 内部静态函数将覆盖外部函数
  • 语言链接性

    • C语言对函数名的矫正使用的约定与C++不同, 可用函数原型指出其使用的约定
    extern "C" void fun(int);   // 使用C语言的约定
    extern void fun(int);     // 使用C++的约定
    extern "C++" void fun(int); // 使用C++的约定 -- 显式指出
  • 内联函数不受单定义规则约束 – 所以可以放在头文件中

动态内存分配

  • new 运算符

    • 内置的标量类型 – 可以加括号分配内存并初始化 – 也适用于有合适构造函数的类

      int* p = new int(5); // 初始化 *p 的值为5
    • 初始化结构体或数组 – 使用大括号的列表进行初始化(C++11)

      node *p = new node {1.0, 2.0};    // node 为结构体
      int * p = new int[4] {1, 2, 3, 4};  // 初始化数组
    • new 失败时
      • 返回空指针 – 旧
      • 引发 std::bad-alloc 异常 – 第15章
    • new 运算符调用如下函数 – 分配函数

      void* operator new(std::size_t);   // new
      void* operator new[](std::size_t,); // new[]
      • delete相有相应的释放函数

  • 定位 new 运算符

    • new 负责在堆上找一块满足要求的内存块
    • 定位new 能指定要使用的位置
      • 设置内存管理规程,处理需要通过特定地址进行访问的硬件,在特定位置创建对象等
    • 使用

      
      #include 
      
      char buff[50];
      int* p = new (buff) int[20]; // 从buff中分配一个包含20个int的数组
      • 不能使用delete来释放使用定位new分配的内存
    • 定位new 相当于将分配内存的重任交给了程序员
    • 定位new 的分配函数

      void* operator new(std::size_t, void* position);

名称空间

  • 一些概念

    • 声明区域
      • 可以在其中声明的区域
      • 全局变量的声明区域为其声明所在的文件,局部变量为其代码块
    • 潜在作用域
      • 从声明点开始,到其声明区域的结尾
    • 作用域
      • 变量对程序而言可见的的范围
      • 潜在作用域可能会被嵌套的声明区域隐藏 – 如局部变量隐藏全局变量

  • 新特性 – 可命名的名称空间

    • 名称空间可以是全局的, 也可以位于另一个名称空间中,但不能位于代码块中
    • 默认在名称空间中声明的名称的链接性为外部的(除了const)
    • 全局名称空间 – 对应于文件级声明区域, 即之前的全局变量所在空间
    • 名称空间是开放的, 即可在不同的地方声明同一个名称空间,整个名称空间为累计起来的结果
    • 访问名称空间中名称的方法 – 使用作用域解析运算符 – ::
    namespace CE {
        int x;
        double y;
    }
    cout << CE::x << endl;

  • using 声明 和 using 编译指令 – 简化名称的使用

    • using 声明 – 使特定的标识符可用

      using CE::x;
      • 其实质是将特定的名称添加到它所属的声明区域中
      • 如果该声明区域中已经 存在同名变量,则会报错
    • using 编译指令 – 使该名称空间的所有名称都可用

      using namespace CE;
      • 可在全局使用,也可在代码块中使用
      • 使用此编译指令时,局部名称会隐藏名称空间名称
      • < iostream > 与 < iostream.h > 的区别是前者支持名称空间
      using namespace CE;    // 如果使用using CE::x; 则会报错
      int x = 2;
      cout << x << endl;    // 局部变量x -- 将隐藏CE中的x
      cout << CE::x << endl;  // 名称空间中的x
  • 其它特性

    • 名称空间是可嵌套的
    • using 编译指令是可传递的
    • 可以给名称空间创建别名 – 常用于简化嵌套名称空间的使用

      namespace cloud_engine = CE;
    • 可以使用未命名的名称空间
      • 其作用域为整个文件 – 使用时不用加域解析运算符
      • 相当于内部链接的全局静态变量(使用static关键字)

  • 使用名称空间的指导规则

    • 使用在已命名的名称空间中声明的变量,而不是使用外部全局变量
    • 使用在已命名的名称空间中声明的变量,而不是使用静态全局变量
    • 如果开发了一个函数库或类库,将其放在一个名称空间中
    • 仅将编译指令using作为一各路将旧代码转换为使用名称空间的权宜之计
    • 不要在头文件中使用using编译指令
      • 掩盖了要让哪些名称可用
      • 包含头文件的顺序可能影响程序的行为 – 应放在所有的 #include 之后
    • 导入名称时,首选使用作用域解析运算符 或 using声明 的方法
    • 对于 using声明, 首选将其作用域设置为局部而不是全局

你可能感兴趣的:(C/C++)