烤面筋 Day 04

烤面筋 Day 04

单例模式

1. 单例模式是什么?有哪些单例模式?

答:单例模式是一种设计模式,旨在确保一个类在整个应用程序的生命周期当中只有一个实例,提供一个全局访问点来获取该实例。

有一般的单例模式(用 static 修饰成员函数和 static 的局部变量),饿汉式单例模式,懒汉式单例模式,还有就是个人比较常用的 CRTP (声明单例的通用模板类)

2. 分别介绍一下这几种单例模式?

  • 一般单例模式:静态成员函数 + 静态局部变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Singleton {
public:
static Singleton& GetInstance() {
// 静态局部变量只会被初始化一次
static Singleton instance;
return instance;
}
~Singleton() = default;
private:
Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};

  • 饿汉式单例模式:静态成员指针变量 + cpp 文件定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Singleton {
public:
~Singleton() = default;
static Singleton* GetInstance() {
if(instance == nullptr)
instance = new Singleton();
return instance;
}
private:
static Singleton* instance;
Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};

同时在 cpp 文件中定义 instance 实例

1
Singleton* Singleton::instance = Singleton::GetInstance();
  • 懒汉式单例模式: 智能指针 + once_flag
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <mutex>
#include <memory>

class Singleton {
public:
~Singleton() = default;
static std::shared_ptr<Singleton> GetInstance() {
static std::once_flag flag;
// flag 底层采用了原子性的原理,紧接的线程如果发现了 flag 已经被初始化,
// 就不会执行 call_once 后面的可调用对象
std::call_once(flag, [](){
// 这里不能使用 std::make_shared 因为构造函数是私有的,外部作用域无法访问
instance = std::shared_ptr<Singleton>(new Singleton());
});
return instance;
}
private:
static std::shared_ptr<Singleton> instance;
Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};

3. CRTP 是什么?能不能实现一下?

答:是一种将派生类作为模板参数传递给基类的技术,即一个类继承以自身为模板参数的基类。

基类代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <memory>
#include <mutex>

template <typename T>
class Singleton {
protected:
Singleton() = default;
Singleton(const Singleton<T>& other) = delete;
Singleton& operator=(const Singleton<T>& other) = delete;
static std::shared_ptr<T> _instance;
public:
~Singleton() = default;
static std::shared_ptr<T> GetInstance() {
static std::once_flag flag;
std::call_once(flag, [&](){
_instance = std::shared_ptr<T>(new T);
});
return _instance;
}
};

// 注意模板类的 static 一定要在头文件中定义
template <typename T>
std::shared_ptr<T> Singleton<T>::_instance = nullptr;

派生类代码:

1
2
3
4
5
6
7
class SingleNet : public Singleton<SingleNet> {
private:
SingleNet() = default;
friend class Singleton<SingleNet>;
public:
~SingleNet() = default;
};

观察者模式

1. 什么是观察者模式?

答:观察者模式 是一种行为设计模式。它定义了对象间的一种 一对多 的依赖关系,使得每当一个对象状态发生改变时,所有依赖于它的对象都会得到通知并被自动更新。

2. 你觉得里面的重点是什么?

  • 解耦:被观察者(Subject)不需要知道观察者(Observer)的具体类,只需要知道它们实现了某个接口。

  • 触发联动:状态改变自动触发行为,不需要轮询检查。

  • 抽象依赖:Subject 依赖于 Observer 的抽象基类,符合开闭原则(对扩展开放,对修改关闭)。

3. 实现一下观察者模式吧

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <iostream>
#include <vector>
#include <memory>
#include <mutex>
#include <algorithm>
#include <string>

// 1. 抽象观察者
class Observer : public std::enable_shared_from_this<Observer> {
public:
virtual ~Observer() = default;
virtual void update(const std::string& state) = 0;
};

// 2. 被观察者(Subject)
class Subject {
public:
// 线程安全的订阅:使用 weak_ptr 记录观察者
void attach(std::weak_ptr<Observer> observer) {
std::lock_guard<std::mutex> lock(_mtx);
_observers.push_back(observer);
}

// 状态更新并通知
void setState(std::string state) {
std::vector<std::shared_ptr<Observer>> active_observers;
{
std::lock_guard<std::mutex> lock(_mtx);
_state = state;

// 遍历并检查生命周期
auto it = _observers.begin();
while (it != _observers.end()) {
// 尝试提升为 shared_ptr
if (auto obj = it->lock()) {
active_observers.push_back(obj);
++it;
} else {
// 观察者已销毁,自动清理从列表中剔除
it = _observers.erase(it);
}
}
}

// 在锁外进行通知,避免死锁风险及长时间占用锁
for (const auto& obs : active_observers) {
obs->update(_state);
}
}

private:
std::string _state;
std::vector<std::weak_ptr<Observer>> _observers; // 核心:弱引用
std::mutex _mtx; // 核心:互斥锁保证线程安全
};

// 3. 具体观察者示例
class Worker : public Observer {
public:
Worker(std::string name) : _name(name) {}
void update(const std::string& state) override {
std::cout << "Worker " << _name << " received: " << state << std::endl;
}
private:
std::string _name;
};

Inline 内联函数

1. 内联函数的实现机制?

答:内联函数是向编译器发出的一个建议,请求将函数调用替换为函数体本身。

目的是消除函数调用的开销(如压栈、跳转、返回等),对于频繁调用的小函数性能提升明显。

2. 编译器一定会内联吗?

答:不一定会内联。编译器会根据复杂的启发式算法自行决定。

如果函数过大或者过于复杂或者函数是虚函数(内联是编译时确定,虚函数是运行时确定的)的情况下,编译器会拒绝内联。同时现代的编译器非常聪明,即使没有写 inline 关键字,只要它认为有益且符合条件,也会自动进行优化内联


烤面筋 Day 04
https://dxblacksmith.github.io/2026/01/16/烤面筋_Day04/
作者
DxBlackSmith
发布于
2026年1月16日
许可协议