cs106l-lecture-5-1-classes-and-const-correctness
Course: 斯坦福大学: C++标准库编程 | C++ Standard Library Programming
🎮 CS106L Lecture 5.1 — Classes and Const Correctness
🏛 课程:Stanford CS106L — Standard C++ Programming Language
📍 主题:类与 const 正确性(Classes & Const Correctness)
🎯 目标:理解类的封装机制与 const 修饰符的真正含义,学会编写类型安全且可维护的面向对象代码。
🧭 课程定位
在 STL 模块中,我们通过泛型算法实现了“函数式抽象”。
从这一讲开始,我们进入 类与对象的世界(OOP 抽象),
即:如何用类型表达思想。
“函数抽象行为,类抽象存在。”
本讲的关键思想是:
💡 “正确使用 const,让代码更安全、更语义化。”
🏷 关键词速记
Class|类
Object|对象
Encapsulation|封装
Const Correctness|常量正确性
Immutable Object|不可变对象
Const Method|常量成员函数
Const Iterator|常量迭代器
RAII|资源自动管理
🎮 30 秒课程摘要
“const 是 C++ 的契约系统。
它告诉编译器——这里的数据不会被改动,
也告诉团队——你可以信任这段代码。”
📚 核心内容讲解
🧩 1️⃣ 类(Class)回顾与结构
C++ 的类(class)是封装数据与函数的抽象体。
📦 示例 1:类的基本形式
// Student.h
class Student {
public:
Student(std::string name, int id);
void print() const; // const 方法
private:
std::string name_;
int id_;
};
// Student.cpp
#include "Student.h"
#include <iostream>
Student::Student(std::string name, int id)
: name_(name), id_(id) {}
void Student::print() const {
std::cout << name_ << " (" << id_ << ")\n";
}🧩 讲解:
- 类的声明放在
.h,实现放在.cpp。 - 构造函数使用初始化列表(Initializer List)。
print()后的const表示:该成员函数不会修改对象状态。
🧩 2️⃣ const 的四种语义场景
| 使用位置 | 示例 | 含义 |
|---|---|---|
| 1️⃣ 常量变量 | const int n = 10; | 变量值不可变 |
| 2️⃣ 常量指针 | int *const p = &n; | 指针本身不能改 |
| 3️⃣ 指向常量的指针 | const int* p = &n; | 不能通过指针改值 |
| 4️⃣ 常量成员函数 | void print() const; | 不修改成员变量 |
📦 示例 2:四种 const 语义
int a = 5;
const int* p1 = &a; // *p1 只读
int* const p2 = &a; // p2 不可变
const int* const p3 = &a; // 双重保护
🧠 口诀:
“const 贴谁,谁不变。”
即:
const int*→ 指向的内容不变int* const→ 指针自身不变
🧩 3️⃣ 成员函数的 const 正确性
成员函数可以被声明为 const,
表示它不会修改类的状态。
📦 示例 3:const 成员函数
class Circle {
public:
Circle(double r) : radius(r) {}
double getArea() const {
return 3.14159 * radius * radius;
}
private:
double radius;
};📦 错误示例:
double getArea() const {
radius = 0; // ❌ error: assignment of member in const function
}🧩 编译器行为:
编译器在 const 成员函数中将 this 指针视为:
const Circle* const this;→ 即函数体中无法修改成员变量。
🧩 4️⃣ const 对象与成员函数的绑定规则
| 对象类型 | 允许调用的函数 |
|---|---|
| 非 const 对象 | 所有函数 |
| const 对象 | 仅可调用 const 成员函数 |
📦 示例 4:const 对象调用规则
const Circle c1(5);
c1.getArea(); // ✅ const 函数可用
c1.setRadius(10); // ❌ 非 const 函数禁止
🧠 记忆口诀:
“const 对象,只信 const 方法。”
🧩 5️⃣ 可变成员变量 mutable
有时需要在 const 方法中修改某些状态(如缓存、计数器)。
此时可以用 mutable 修饰。
📦 示例 5:mutable 用法
class Cache {
public:
double get() const {
++accessCount; // ✅ allowed due to mutable
return data;
}
private:
double data = 3.14;
mutable int accessCount = 0;
};🧩 应用场景:
- 缓存(lazy evaluation)
- 调试或统计访问次数
🧩 6️⃣ const 与迭代器
C++ 容器提供两种迭代器:
| 类型 | 说明 |
|---|---|
iterator | 可读写 |
const_iterator | 只读(不能修改元素) |
📦 示例 6:使用 const_iterator
std::vector<int> v = {1, 2, 3};
for (std::vector<int>::const_iterator it = v.begin(); it != v.end(); ++it)
std::cout << *it << " ";📤 输出:1 2 3
📦 错误示例:
*it = 5; // ❌ 不能修改 const_iterator
🧠 实用建议:
- 函数参数若不修改容器 → 使用
const std::vector<T>& - 遍历时若不修改元素 → 使用
const_iterator
🧩 7️⃣ const 参数与返回值
📦 示例 7:函数签名中的 const
void print(const std::vector<int>& v) {
for (auto& x : v) std::cout << x << " ";
}
const std::string getName() const; // 不推荐:返回值拷贝时 const 无意义
🧩 规则总结:
- 参数:使用
const &避免拷贝且保证只读 - 返回值:若按值返回,
const多余;若返回引用,const可防止修改原对象
🧩 8️⃣ const_cast — “黑魔法”
const_cast 可移除 const 限制,但风险极高。
📦 示例 8:const_cast
void foo(const int* p) {
int* q = const_cast<int*>(p);
*q = 99; // ⚠️ 未定义行为
}🧩 用法建议:
- 仅在必须与旧 API(如 C 函数)交互时使用
- 在现代 C++ 中应尽量避免
🧠 口诀:
“const_cast 是炸药,用错就爆炸。”
🧩 9️⃣ RAII 与 const 的结合
RAII(Resource Acquisition Is Initialization)保证资源的自动释放,
与 const 一起可形成安全的不变式。
📦 示例 9:RAII + const
class FileGuard {
public:
FileGuard(const std::string& path)
: file(fopen(path.c_str(), "r")) {}
~FileGuard() { if (file) fclose(file); }
FILE* get() const { return file; }
private:
FILE* file;
};🧩 讲解:
- 构造即获取资源
- 析构即释放
get()为const,保证访问安全
🧠 记忆口诀
“const 定界线,改动受约束;
mutable 留活口,安全有章数。”
🧪 对照实验区
✅ 实验 1:const 成员函数防止意外修改
class A {
public:
int get() const { return x; }
void set(int val) { x = val; }
private:
int x = 10;
};
const A a;
a.get(); // ✅ OK
a.set(5); // ❌ error
✅ 实验 2:mutable 的例外机制
class B {
public:
void access() const { ++count; }
int getCount() const { return count; }
private:
mutable int count = 0;
};
B b;
b.access();
std::cout << b.getCount(); // 输出 1 ✅
🪄 工程与设计建议
- 在类中标记所有不会修改状态的函数为
const。 - 优先使用
const &参数传递,避免拷贝与副作用。 - 对内部计数器或缓存使用
mutable,谨慎设计不变式。 - 使用
const_iterator表明“只读遍历”意图。 - 禁止滥用
const_cast,若必须使用,应隔离在单独函数中。 - RAII + const 是现代 C++ 内存安全的基础。
✅ 小结表
| 场景 | 写法 | 效果 |
|---|---|---|
| 常量对象 | const T obj; | 只读 |
| 成员函数 | void func() const; | 不可修改成员 |
| 指针常量 | T* const p; | 指针地址不可改 |
| 指向常量的指针 | const T* p; | 不可修改目标内容 |
| 成员变量可变 | mutable T x; | const 方法中可改 |
| 只读遍历 | const_iterator | 防止误写容器 |
“C++ 的 const 不只是修饰符,而是契约、文档与编译期安全保障。”
是否希望我继续整理下一讲
👉 Lecture 5.2 — Class Templates & Type Traits
(类模板、偏特化、enable_if、type traits、模板元编程基础)
保持同样 Notion 格式与代码解释?