《C++ Primer Plus》9. 内存模型和命名空间
存储持续性、作用域与链接性
存储持续性:
- 自动存储持续性:函数定义中声明的变量及参数
- 静态存储持续性:函数定义外定义的变量、使用
static
关键字定义的变量 - 线程存储持续性:使用
thread_local
关键字声明的变量 - 动态存储持续性:使用
new
分配的变量
作用域:局部、文件、类、命名空间
链接性:外部、内部、无
局部、无链接性 | 文件作用域、内部链接性 | 文件作用域、外部链接性 | |
---|---|---|---|
自动存储持续性 | 函数中声明的函数参数及变量 | ||
静态存储持续性 | 代码块中被 static 修饰的变量 | 1. 代码块外部被 static 修饰的变量2. 代码块外部被 const 修饰,但不被 extern 修饰的变量 | 1. 代码块外部被 extern const 修饰的变量2. 代码块外部未被任何关键字修饰的变量 3. 代码块外部只被 extern 修饰,并且进行了初始化的变量 |
以下是一些例子:
// 外部链接性
extern const int x1 = 1;
// 内部链接性
const int x2 = 2;
// 内部链接性
static int x3 = 3;
// 外部链接性
int x4 = 4;
// 引用
extern int x5;
// 外部链接性,与 int x6 = 6; 等价
extern int x6 = 6;
引用声明
单定义规则(One Definition Rule):变量只能有一次定义。
根据 ODR,C++ 提供了两种变量声明:定义声明、引用声明。引用声明需要使用 extern
关键字修饰,并且不能进行初始化。
引用时优先本文件作用域中的变量:如果本文件中有 static
修饰的全局变量,那么会优先引用它。
// main.cpp
#include <iostream>
static int x = 10;
int main() {
extern int x;
std::cout << x << std::endl;
return 0;
}
// test.cpp
int x = 20;
上述代码会输出 10
说明符与限定符
volatile
:向编译器表示“即使程序没有更改对应的内存单元,其值也可能发生变化”,应用场景比如两个程序共享内存、硬件串行端口等。如果不使用该关键字,编译器会优化“多次查找”,中间将该变量缓存到寄存器中。
mutable
:用来指出“即使结构体或类的变量被 const
修饰,某成员也可以被修改”
struct Data {
char name[30];
mutable int accesses;
...
};
const Data veep = { "Name1", 0, ... };
strcpy(veep.name, "Name2"); // not allowed
veep.accesses++; // allowed
函数和链接性
static
可将函数的链接性设置为内部
static
修饰函数声明和函数定义有不同的含义:
- 当
static
修饰函数声明时,它告诉编译器去哪里寻找这个函数,如果声明带有static
,则只在该文件中寻找 - 当
static
修饰函数定义时,它告诉编译器这个函数定义的链接性,带有static
时为内部链接性,不带时是外部链接性
// main.cpp
#include <iostream>
static int func() {
return 1;
}
static int func();
int main() {
int x = func();
std::cout << x << std::endl;
return 0;
}
// test.cpp
int func() {
return 2;
}
上面的代码会正常运行,输出 1
// main.cpp
#include <iostream>
int func() { ... }
static int func();
int main() { ... }
// test.cpp
int func() { ... }
上面代码会报错,因为单定义规则不允许两个签名相同的函数
// main.cpp
#include <iostream>
static int func();
int main() { ... }
// test.cpp
int func() { ... }
上面代码会报错,因为编译器在 main.cpp 中找不到 func
函数的定义
// main.cpp
#include <iostream>
int func();
int main() { ... }
// test.cpp
static int func() { ... }
上面代码会报错,因为 test.cpp 中 func
函数是内部链接性
命名空间
C++ 有默认的命名空间,叫做全局命名空间(Global Namespace),全局变量这些东西默认放在全局命名空间中。
不同的命名空间中的名称相互独立,在链接时不可见
// main.cpp
int func();
int main() {
func();
return 0;
}
// test.cpp
namespace X {
int func() {
return 1;
}
}
上面代码会报错,因为 main.cpp 中 func
的声明放在全局命名空间中,test.cpp 中 func
的声明放在 X
命名空间中,在链接时不可见
using
声明:using NS::func;
,声明func
这个名称,如果该名称已经声明,则会编译错误using
编译指令:using namespace NS;
,将命名空间中的所有名称导入,如果某个名称已经声明,那么局部版本会隐藏命名空间版本
不要在头文件中使用 using
关键字
未命名空间:内部链接性,文件作用域,下面的两段代码等价:
namespace {
int counts;
}
int main() { ... }
static int counts;
int main() { ... }