Primer Plus 内存模型和名称空间

单独编译

链接编译模块时,需确保所有对象文件或库都是由同一个编译器生成的

因为不同编译器可能用不同方式实现名称修饰

1
2
3
4
5
6
7
8
9
10
11
12
// Rectangle.h
#ifndef RECTANGLE_H
#define RECTANGLE_H

struct Rectangle {
int length;
int width;
};

void printRectangle(const Rectangle& r);

#endif
1
2
3
4
5
6
7
// Rectangle.cpp
#include <iostream>
#include "Rectangle.h"

void printRectangle(const Rectangle& r) {
std::cout << "Length: " << r.length << ", Width: " << r.width << '\n';
}
1
2
3
4
5
6
7
8
#include <iostream>
#include "Rectangle.h"

int main() {
Rectangle r{10, 5};
printRectangle(r);
return 0;
}

执行 g++ Lab.cpp Rectangle.cpp 后会发生如下:

  • 预处理器将包含的文件与源代码文件合并,产生临时文件temp1.cpp和temp2.cpp

  • 编译器生成每个源代码文件的目标代码文件Lab.o和Rectangle.o

  • 链接程序将目标代码文件、库代码和启动代码合并,生成可执行文件a.out

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

自动存储持续性:函数定义中声明的变量,出了定义域就自动释放

静态存储持续性:函数定义外定义的变量和static修饰的变量,在程序整个运行过程中都存在

线程存储持续性(C++11):thread_local修饰的变量,生命周期和所属线程一样长

动态存储持续性:用new分配的内存将一直存在,直到用delete释放或程序结束

自动变量没有链接性

静态变量有外部链接性(可在其他文件中访问),内部链接性(只能在当前文件中访问)和无链接性(只能在当前函数或代码块中访问)

注意static用于局部声明时,表示存储持续性,用于代码块外的声明时,表示内部链接性

1
2
3
4
5
6
7
8
int global = 1000; // 静态存储,外部链接
static int one_file = 50; // 静态存储,内部链接
void f() {
static int count = 0; // 静态存储,无链接
}
int main() {
..
}

extern 关键字

引用声明其他文件的外部变量,不分配存储空间,且不进行初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
// Lab.cpp
#include <iostream>

extern int lab2;
extern void printlab2();

int main() {
std::cout << lab2 << '\n';
lab2 = 5;
printlab2();
std::cout << lab2 << '\n';
return 0;
}
1
2
3
4
5
6
7
8
// Lab2.cpp
#include <iostream>

int lab2 = 2;

void printlab2() {
std::cout << lab2 << '\n';
}

在变量前面加上::可以显式地使用该变量的全局版本

1
2
3
4
5
6
7
8
9
#include <iostream>

int x = 5;

int main() {
int x = 1;
std::cout << ::x << ' ' << x << '\n'; // 5 1
return 0;
}

两个文件中有一个同名的外部变量,将无法通过编译

需将其中一个加上static,静态变量将隐藏外部变量

说明符和限定符

volatile关键字表明变量的内存单元即使没有被代码修改,其值也可能改变(由于硬件),告诉编译器不要进行神秘的优化

mutable关键字指出即使结构或变量为const,其某个成员也可被修改(珂朵莉树有用到来着)

const全局变量的链接性为内部的(就是说const全局定义蕴含了static),想要其链接性为外部必须显式地用extern关键字,其他文件也必须用extern来引用声明

函数和链接性

函数都默认是静态持续,外部链接性的,也可用static显示定义为内部链接的

namespace

namespace可以是全局的,也可以在另一个namespace里,但不能在代码块里

在namespace里声明的名称的链接性默认为外部的

namespace是开放的,可以一段一段写

指导原则

用namespace里声明的变量,而不是外部全局变量和静态全局变量

1
2
3
4
5
6
7
8
9
10
11
// config.h
#ifndef CONFIG_H
#define CONFIG_H

namespace AppConfig {
// 在namespace中声明变量
extern int screenWidth; // 声明,实际定义在另一个文件中
extern int screenHeight;
}

#endif
1
2
3
4
5
6
7
8
// config.cpp
#include "config.h"

namespace AppConfig {
// 在namespace中定义变量
int screenWidth = 800;
int screenHeight = 600;
}
1
2
3
4
5
6
7
8
9
#include <iostream>
#include "config.h"

int main() {
// 使用namespace中的变量
std::cout << "Screen Width: " << AppConfig::screenWidth << std::endl;
std::cout << "Screen Height: " << AppConfig::screenHeight << std::endl;
return 0;
}