这一章讲三个让代码更安全、更有条理的工具:
const(保护数据)、作用域(变量在哪有效)、命名空间(避免名字冲突)。理解它们,代码质量和可读性会上一个台阶。
10.1 const:保护数据不被修改
const是"常量"的意思,用来声明"这个值不许改"。
const 变量
constintMAX=100;// MAX = 200; // 错误!const 变量不能修改const 参数
函数参数加const,保证函数内不改这个参数:
voidprint(conststd::string&s){// s = "改它"; // 错误!s 是 const,不能改std::cout<<s<<std::endl;}const 成员函数
类的方法加const,表示"这个方法不会修改对象的成员":
classRectangle{doublew,h;public:doublearea()const{// const 方法:只读,不改成员returnw*h;}};const方法只能调用其他const方法,编译器会帮你把关。
核心思想:用
const表达"我承诺不改",让编译器帮你守住承诺,把 bug 消灭在编译期。
10.2 作用域:变量在哪里有效
作用域(scope)决定了变量在代码的哪一段可见、可用。
花括号{}形成作用域
#include<iostream>intmain(){intx=10;// x 在整个 main 里有效{inty=20;// y 只在这个花括号内有效std::cout<<x<<" "<<y<<std::endl;// 都能访问}// std::cout << y; // 错误!y 已经出了作用域,不存在了return0;}输出10 20。
变量在其声明的花括号{}内有效,出了花括号就"销毁"了。
全局变量 vs 局部变量
#include<iostream>intglobal=100;// 全局变量:整个文件都可见intmain(){intlocal=10;// 局部变量:只在 main 里可见std::cout<<global<<" "<<local<<std::endl;// 100 10}建议:尽量用局部变量,少用全局变量。全局变量到处都能改,容易出 bug、难追踪。
10.3 命名空间:避免名字冲突
想象两个人都给变量起名PI,放在一起就冲突了。命名空间(namespace)就是给代码划"区域",隔离同名。
#include<iostream>namespacemathA{constdoublePI=3.14;}namespacemathB{constdoublePI=3.14159;}intmain(){std::cout<<mathA::PI<<std::endl;// 3.14std::cout<<mathB::PI<<std::endl;// 3.14159,同名不冲突}用命名空间::名字精确指定要哪个。
using:简化前缀
std::写多了烦,可以用using:
// using 声明:只引入一个名字(推荐)usingstd::cout;cout<<"你好"<<std::endl;// using 指令:引入整个命名空间(省事但易冲突,慎用)usingnamespacestd;cout<<"直接用 cout"<<endl;建议:初学时,尽量写全
std::,养成好习惯。using namespace std;虽然省事,但在大项目里容易引发名字冲突。
std 和 Eigen 命名空间
现在你就懂了:
std::cout、std::string:标准库里的东西,都在std命名空间Eigen::MatrixXd:Eigen 库的东西,都在Eigen命名空间
这就是为什么写 Eigen 代码时前面都有Eigen::。
10.4 头文件与多文件组织(了解)
真实项目不止一个 .cpp 文件,代码会拆分组织:
project/ ├── main.cpp # 主程序 ├── math_utils.h # 头文件:声明函数和类 └── math_utils.cpp # 源文件:实现头文件(.h)放声明,源文件(.cpp)放实现。其他文件要使用时,#include头文件即可。
// math_utils.h —— 头文件#pragmaonce// 防止头文件被重复包含intadd(inta,intb);// 只声明,不实现// math_utils.cpp —— 源文件#include"math_utils.h"intadd(inta,intb){// 实现returna+b;}// main.cpp#include<iostream>#include"math_utils.h"// 引入自己的头文件intmain(){std::cout<<add(3,5)<<std::endl;// 8}#pragma once防止同一个头文件被多次包含(避免重复定义)。#include "..."(双引号)用于自己项目的头文件,#include <...>(尖括号)用于系统/库头文件。
这三个文件要一起编译——把两个 .cpp 都交给编译器,链接器才能找到add的实现:
g++-std=c++17 main.cpp math_utils.cpp-oapp.exe ./app.exe# 输出 8漏掉math_utils.cpp会得到一个典型的链接期错误(编译能过,链接失败):
undefined reference to `add(int, int)'MSVC 写法:cl /std:c++17 /EHsc /utf-8 main.cpp math_utils.cpp。文件更多时就不用手动列了,交给 CMake(附录 C.6~C.7)。
10.5 小结
const保护数据:const 变量、const 参数、const 方法。- 作用域:变量在声明它的
{}内有效。 - 命名空间:隔离同名,
Eigen::、std::都是命名空间。 - 多文件:头文件放声明,源文件放实现,
#include引入。
下一章,学习动态内存与智能指针——在运行时灵活分配内存而不泄漏。
练习题
- 写一个
const变量,尝试修改,看报什么错。 - 写两个命名空间,里面各放一个同名变量,分别访问。
- 写一个类,给它加一个
const成员函数,说明它为什么不能修改成员。 - 把一个函数拆到头文件和源文件里,在 main 里调用它。