第九章:内存模型和命名空间 | C++ Primer Plus 重点带看
程序的文件类型
1、头文件可以放:
- 结构体/类声明
- 函数原型声明
- 模板定义
- 内联函数定义
- const 常量定义(内部链接)
- 宏定义
- 静态变量,不会重定义,但每个源文件获得的说独立副本
- const 值,内部链接,每个源文件使用的独立副本
禁止防止:普通变量定义、非内联函数定义(会导致重定义)。
2、源文件:
- 存放函数实现和变量定义。
- 存放调用与结构相关的函数代码。
存储特性
1、自动存储:函数参数和局部变量,存储在栈上,作用域为局部,无链接性,
2、静态存储:全局变量和静态变量,存储在数据段/BSS段,程序启动时分配,结束时释放。(零初始化:未显式初始化的静态变量自动初始化为0(指针为nullptr))
3、动态存储: 常规 new 分配(失败抛出异常 std::bad_alloc 或指定 std::nothrow。区别于定位 new 分配)
char buffer[sizeof(string)];
string* p2 = new(buffer) string("hello");
p2->~string(); // 必须手动析构,不手动析构还是会造成类型内部的资源泄漏。因为编译器不知道 buffer上有个对象需要析构,不会自动调用对象的析构函数。仍旧需要程序员自己手动管理内存。
4、线程存储:thread_local 声明的变量,每个线程拥有独立副本,生命周期与线程相同。
#include <iostream>
#include <thread>
using namespace std;
thread_local int tCount = 0; // 线程局部存储
void func(int id) {
tCount = id * 10;
cout << "线程" << id << ": tCount = " << tCount << endl;
}
int main() {
thread t1(func, 1);
thread t2(func, 2);
thread t3(func, 3);
t1.join();
t2.join();
t3.join();
cout << "主线程: tCount = " << tCount << endl;
}
输出:
线程1: tCount = 10
线程2: tCount = 20
线程3: tCount = 30
主线程: tCount = 0
链接性
1、外部链接:可在多个文件间共享,其他文件通过 extern 声明访问。
// a.cpp
int global = 10; // 外部链接
void func() {} // 外部链接
// b.cpp
extern int global; // 声明外部变量
void func(); // 声明外部函数
int main() {
cout << global << endl; // 访问 a.cpp 的 global
func(); // 调用 a.cpp 的 func
}
2、内部链接:只能在定义的文件内部使用
// a.cpp
static int fileVar = 10; // 内部链接(static 全局变量)
static void helper() {} // 内部链接(static 函数)
const int MAX = 100; // 内部链接(const 全局变量,默认)
constexpr int VAL = 42; // 内部链接
namespace {
int anonVar = 20; // 内部链接(匿名命名空间)
}
// b.cpp
extern int fileVar; // 链接错误!找不到
3、无链接:只在作用域内可见
void func() {
int local = 10; // 无链接(局部变量)
static int count = 0; // 无链接(静态局部变量,但有静态存储)
}
函数的作用域
函数只能定义在全局、命名空间或类中,不能在代码块内定义普通函数。Lambda 表达式和局部类成员函数是例外情况。
void outer() {
auto inner = []() { cout << "lambda" << endl; }; // Lambda
inner(); // 调用
}
void outer() {
struct Local { // 局部类
void method() {} // 局部类的成员函数
};
Local obj;
obj.method();
}
函数的默认存储和链接性
默认存储是静态存储,连接性是外部链接。但可以通过用 static 修饰改为内部链接,限制在本文件内使用。(常用于隐藏实现细节的辅助函数)
内联函数
可以在多个翻译单元有相同的定义,实际上改变了链接规则,即使内部变量是静态变量也不会产生多个实例。
// header.h
inline void func() {
static int count = 0; // 静态局部变量
cout << ++count << endl;
}
// a.cpp
#include "header.h"
void testA() { func(); }
// b.cpp
#include "header.h"
void testB() { func(); }
// main.cpp
#include "header.h"
int main() {
func(); // 输出 1(count: 0 → 1)
testA(); // 输出 2(count: 1 → 2)
testB(); // 输出 3(count: 2 → 3)
}
static 关键字作用
位置 | 存储特性 | 链接性 | 说明 |
全局变量前 | 静态(不变) | 外部→内部 | 限制在本文件 |
函数前 | 静态(不变) | 外部→内部 | 限制在本文件 |
局部变量前 | 自动→静态 | 无 | 延长生命周期 |
类成员变量前 | 静态 | 外部 | 属于类,所有实例共享 |
类成员函数前 | — | — | 属于类,无 this 指针 |
// 1. 全局 static 变量
static int fileVar = 10; // 静态存储、内部链接
// 2. static 函数
static void helper() {} // 静态存储、内部链接
// 3. 局部 static 变量
void func() {
static int count = 0; // 静态存储、无链接
}
// 4. 类 static 成员
class A {
static int sVar; // 静态存储、外部链接
static void method(); // 属于类
};
cv 限定符
const 全局变量默认内部链接,可用 extern 改为外部链接。
// a.cpp extern const int MAX = 100; // 强制外部链接 // b.cpp extern const int MAX; // 声明即可使用 cout << MAX << endl; // 输出 100
volatile 防止编译器优化,确保每次都从内存读取,但不保证原子性,多线程应使用 std::atomic。
volatile int flag = 0;
// 等待中断或其他线程修改 flag
while (flag == 0) {
// 没有 volatile,编译器可能优化为:
// if (flag == 0) while (1); // 死循环
// 有 volatile,每次都从内存读取 flag
}
两种变量声明
定义声明(定义):分配存储空间,只能有一次。
引用声明(声明,extern):不分配内存,引用已有变量,可多次。
作用域解析运算符(::)
作用域解析运算符 :: 用于:全局作用域(::var 访问全局变量)、命名空间作用域(NS::member)、类作用域(Class::member)以及嵌套命名空间访问。
命名空间
命名空间只能定义在全局或嵌套在其他命名空间中,不能位于代码块内。命名空间内成员默认外部链接,匿名命名空间成员为内部链接(等价于 static),常用于隐藏本文件内的实现细节。
命名空间可以放在头文件,但内部成员遵循与普通变量/函数相同的规则:声明放头文件,定义放源文件。变量定义和非 inline 函数定义在头文件会导致重定义错误。const 变量、inline 函数、static 变量、匿名命名空间成员可在头文件定义。(const、static、匿名命名空间成员产生独立副本;inline 函数被链接器合并为同一实体。)
// ✓ 全局位置
namespace GlobalNS {
int value = 10;
}
// ✓ 嵌套在另一个命名空间中
namespace Outer {
namespace Inner {
int value = 20;
}
}
// ✗ 不能位于局部代码块
void func() {
// namespace LocalNS { int x = 10; } // 错误!
}
namespace MyNS {
int a = 10; // 外部链接
static int b = 20; // 内部链接
const int c = 30; // 内部链接
void func() {} // 外部链接
static void helper() {} // 内部链接
}
namespace {
int value = 10; // 内部链接,等价于 static int value = 10
void func() {} // 内部链接,等价于 static void func()
}
// 其他文件无法访问这些成员
C++ Primer Plus 精读|从入门到面试,重点内容全程带看。 本专栏以《C++ Primer Plus》为蓝本,逐章提炼必考知识点、易错点、面试高频考点,跳过冗余示例,直击语法本质与工程实践,帮你高效吃透 C++ 基础,夯实底层开发必备能力。
