C++PrimerPlus6-09-内存模型和名称空间

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

  • 单独编译
  • 存储持续性、作用域和链接性
  • 定位new运算符
  • 名称空间

9.1 单独编译

将组件函数放在独立的文件中,可以单独编译这些文件,然后将他们链接乘可执行的程序。
将程序分成三部分:

  • 头文件:包含结构声明和使用这些结构的函数的原型
  • 源代码文件:包含与结构有关的函数的代码
  • 源代码文件:包含调用与结构相关的函数的代码

头文件中常包含的内容:

  • 函数原型
  • 使用#define或const定义的符号常量
  • 结构声明
  • 类声明
  • 模板声明
  • 内联函数

包含自己的头文件时,使用引号而不是尖括号(尖括号编译器会去存储标准头文件的地方找,双引号会在工作目录或源码目录找)。
头文件管理:在头文件中,使用#ifndef(即if not defined)来避免包含多次同一个头文件:

#ifndef COORDIN_H_
#define COORDIN_H_
// 头文件内容
#endif

9.2 存储持续性、作用域和链接性

复习第4章,C++使用3种(C++11是4种)方案来存储数据:

  • 自动存储持续性:在函数定义中声明的变量(包括函数参数)。
    • 开始执行其函数或代码块时创建,执行完后释放
  • 静态存储持续性:在函数定义外定义的变量和使用关键字static定义的变量。
    • 在程序整个运行过程中都存在。
  • 动态存储持续性:用new分配的内存。
    • 将一直存在,直到使用delete或程序结束。又被称为自由存储(free store)或堆(heap)
  • 线程存储持续性(C++):多线程,变量用thread_local声明
    • 生命周期与所属线程一样长。

9.2.1 作用域和链接

作用域(scope)描述了名称在文件的多大范围内可见。
链接性(linkage)描述了名称如何在不同单元间共享。

9.2.2 自动存储持续性

默认情况下,函数声明的函数参数和变量的存储持续性为自动,作用域为局部,没有链接性。
栈,后进先出。

9.2.3 静态持续变量

静态存储持续性变量有3中链接性:外部链接性、内部链接性、无链接性。寿命长,在整个程序执行期间一直存在。

  • 外部链接性:在代码块外面声明。所有文件可用
  • 内部链接性:在代码块外部声明,并使用static。单文件可用
  • 无链接性:在代码块内部声明,并使用static。
int global = 1000;            // static duration, external linkage 外部链接性
static int one_file = 50;    // static duration, internal linkage 内部链接性
void funct1(int n)
{
    static int count = 0;    // static duration, on linkage 无链接性
}

9.2.4 静态持续性、外部链接性

外部变量(全局变量)。
每个使用外部变量的文件,都必须声明他,但因为单定义规则(One Definition Rule, ODR),只能定义一次:

  • 定义声明(定义):给变量分配存储空间
  • 引用声明(声明):不分配空间,引用已有变量。使用extern
// file01.cpp
extern int cats = 20;     // definition because of initialization
int dogs = 22;            // also a definition
int fleas;                // also a definition

// file02.cpp
// use cats and dogs from file01.cpp
extern int cats;        // not definition because the use
extern int dogs;        // extern and have no initialization

使用域解析运算符::也可访问到全局变量。::warming。
选择全局变量和局部变量:

  • 通常情况使用局部。
  • 全局变量适合月份名数组、原子量数组、通用常量等。

9.2.5 静态持续性、内部链接性

将static用于作用域为整个文件的变量时,该变量的链接性为内部。

9.2.6 静态持续性、无链接性

将static用于代码块内部定义的变量。虽然只在代码块可用,但该代码块不活动时依然存在。因此在两次函数调用之间,静态局部变量的值将保持不变。

9.2.7 说明符和限定符

存储说明符(storage class specifier):auto(C++11不再是说明符), register,static,extern,thread_local(C++11新增),mutable
cv-限定符(cv-qualifier):const,volatile

  • volatile:表示即使程序代码没有对内存单元进行修改,其值也可能变化(例如硬件修改了)。
  • mutable:即使结构或类变量为const,其某个成员也可以被修改。
  • const:默认的全局变量是外部链接性,但是const全局变量是内部链接性。(像用了static一样)
struct data
{
    char name[30];
    mutable int accesses;
};

const data veep = {"Claybourne Clodde", 0};
veep.accesses++; // allowed

9.2.8 函数和链接性

所有函数的存储持续性是静态的。默认情况下,函数的链接性为外部,可以使用static设置为内部。

9.2.9 语言链接性

如果函数名是spiff,C翻译为_spiff;C++执行名称修饰,可能将spiff(int)转换为_spiff_i,将spiff(double, double)转换为_spiff_d_d。
如果在C++程序中用C库编译的函数,在函数原型指出要使用的约定:

extern "C" void spiff(int);        // use C protocol for name look-up
extern void spoff(int);            // use C++ protocol for name look-up 默认方式
extern "C++" void spaff(int);    // use C++ protocol for name look-up 显式

9.2.10 存储方案和动态分配

存储方案可以用来追踪动态内存的自动和静态指针变量。

float* p_fees = new float[20];
extern float* p_fees;

用new进行初始化的写法:

int* pi = new int(6);
int* pii = new int{6}; //C++11
struct where{double x; double y};
where* one = new where{1.1, 2.2}; // C++11

除了普通new,还有个定位(placement)new运算符,需要包含头文件new:

#include <new>
struct chaff
{
    char dross[20];
    int slag;
};
char buffer1[50];
char buffer2[500];
int main()
{
    chaff *p1, *p2;
    int *p3, *p4;
    //first, the regular forms of new
    p1 = new chaff; // place structure in heap
    p3 = new int[20]; // place int array in heap
    //now, the two forms of placement net
    p2 = new (buffer1) chaff; // place structure in buffer1
    p4 = new (buffer2) int[20]; // place int array in buffer2
}

9.3 名称空间

9.3.1 传统的C++名称空间

9.3.2 新的名称空间特性

定义一种新的声明区域来创建命名的名称空间。

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

用using声明或using编译指令,避免每次使用都包含名称空间的限定:::

using Jill::fetch;
using namespace Jack;

局部名称将覆盖名称空间版本。使用using声明比using编译指令更安全,因为后者可能将不必要的东西搞进来了。
可以套娃,可以别名,可以匿名(P用?)。

namespace elements
{
    namespace fire
    {
        int flame;
        ...
    }
}

namespace my_very_favorite_things {...};
namespace mvft = my_very_favorite_things;

namespace // unnamed namespace 提供了链接性为内部的静态变量的替代品
{
    int ice;
}

9.3.3 名称空间示例

9.3.4 名称空间及其前途

指导原则:

  • 使用在已命名的名称空间中声明的变量,而不是外部全局变量或静态全局变量。
  • 如果开发了一个函数库或类库,将其放在一个名称空间中。
  • 仅将编译指令using作为一种将旧代码转换为使用名称空间的权宜之计。
  • 不要在头文件中使用using编译指令。
  • 导入名称时,首选作用域解析符或using声明。
  • 对于using声明,首先将其作用域设置为局部而不是全局。

9.4 总结

  • C++鼓励开发时使用多个文件。h + cpp
  • 储存持续性,作用域和链接性
  • 自动变量
  • 静态变量,extern,static
  • 动态变量,new,delete,自由存储区或堆
  • 名称空间,作用域解析符、using声明、using编译指令

9.5 复习题

9.6 编程练习


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 cdd@ahucd.cn

×

喜欢就点赞,疼爱就打赏

B站 cdd的庇护之地 github itch