这一章一半是函数增强,一半是封装入门。函数增强让 C++ 接口更灵活;封装则把数据和行为放到一起,为后面的对象模型、构造析构和多态打基础。

3.2 默认参数

默认参数让调用者可以省略常用值。

1
2
3
4
5
#include <iostream>

void connect(const std::string &host, int port = 80) {
std::cout << host << ":" << port << std::endl;
}

复盘重点:默认参数通常写在声明处,不要在声明和定义里重复写不同默认值。

3.3 函数重载

函数重载允许同名函数根据参数列表区分。

1
2
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }
能否重载 原因
参数个数不同 可以
参数类型不同 可以
参数顺序不同 可以
只有返回值不同 不可以

3.4 内联函数

inline 表达希望编译器把函数体展开,减少调用开销。它不是强制命令,最终由编译器决定。

1
2
3
inline int square(int x) {
return x * x;
}

小函数可以考虑内联,复杂函数不要为了“快”盲目内联。

3.5 函数模板

函数模板让同一套逻辑适配多种类型。

1
2
3
4
template <typename T>
T max_value(T a, T b) {
return a > b ? a : b;
}

模板不是运行时万能函数,而是编译期按类型生成对应版本。

3.6 递归函数

递归函数要有出口,否则会无限调用直到栈耗尽。

1
2
3
4
5
6
int factorial(int n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}

复盘重点:递归深度、出口条件、重复计算和栈空间。

3.7 回调函数

回调就是“把函数作为参数传进去,之后由别人调用”。它适合事件处理、排序规则、插件式逻辑。

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

void run(void (*callback)(int)) {
callback(100);
}

void print_value(int value) {
std::cout << value << std::endl;
}

现代 C++ 里还会用 lambda、std::function 表示回调。

3.8 面向对象编程

面向对象不是把所有东西都写成类,而是把“数据”和“操作数据的行为”放在一个稳定边界里。

概念 复盘重点
封装 对外暴露必要接口,隐藏内部实现
继承 复用和扩展已有类型
多态 通过基类接口调用派生类行为

3.9 类与结构体

C++ 中 classstruct 的主要默认区别是访问权限:class 默认 private,struct 默认 public。

1
2
3
4
5
6
7
8
class User {
private:
int id;

public:
explicit User(int value) : id(value) {}
int get_id() const { return id; }
};

4.0 类的声明与定义

类可以只声明接口,把实现放到 .cpp 文件。项目里这样更清晰。

1
2
3
4
5
6
7
8
class Counter {
public:
void add();
int value() const;

private:
int count = 0;
};

4.1 类的访问权限

权限 含义
public 类外可访问
protected 类内和派生类可访问
private 只有类内可访问

访问权限不是安全边界,但它能帮助项目保持封装。

4.2 类的作用域

类成员在类作用域内。类外定义成员函数要带 ClassName::

1
2
3
void Counter::add() {
++count;
}

4.3 类的对象模型

对象通常只保存成员变量;普通成员函数在代码区,调用时会隐式传入 this 指针。逆向 C++ 程序时,this、成员偏移、虚表指针都很关键。

内容 在哪里
成员变量 对象内存中
普通成员函数 代码区
this 指针 调用成员函数时隐式传入
静态成员 不属于单个对象

本章最小复盘代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
#include <functional>

class Counter {
public:
explicit Counter(int init = 0) : count(init) {}

void add(int step = 1) {
count += step;
}

int value() const {
return count;
}

private:
int count;
};

template <typename T>
T twice(T value) {
return value + value;
}

void apply(Counter &counter, const std::function<void(Counter &)> &fn) {
fn(counter);
}

int main() {
Counter counter;
apply(counter, [](Counter &c) { c.add(2); });
std::cout << counter.value() << std::endl;
std::cout << twice(21) << std::endl;
return 0;
}

学习检查清单

检查项 状态
能说明默认参数、重载和内联的适用场景 待复盘
能写出简单函数模板 待复盘
能写出带出口条件的递归函数 待复盘
能用类封装数据和行为 待复盘
能解释 this 指针和对象成员布局的关系 待复盘