Skip to content

Commit 45bb1b0

Browse files
committed
Update public notes
1 parent 94a2d7d commit 45bb1b0

File tree

1 file changed

+87
-1
lines changed

1 file changed

+87
-1
lines changed

content/编程相关/编程语言/Cpp 之旅 第三版 读书笔记.md

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1201,4 +1201,90 @@ int get_number(const string& s) {
12011201
12021202
在给定了优秀的哈希函数的情况下,unordered_map 比 map 快得多,尤其是对大型容器而言。
12031203
1204-
#todo
1204+
### 分配器
1205+
*内存管理 + 容器 = 经典优化魔法*
1206+
1207+
默认标准库容器用 new 和 delete 分配空间。这很好,但是某些情况会带来性能问题。
1208+
1209+
> 假定有一个重要的、长时间运行的系统,其使用事件队列(18.4节)并且使用vector作为事件存储容器,元素以shared_ptr保存。在这种情况下,事件的最后一个用户会隐式地释放该事件:
1210+
1211+
```cpp
1212+
struct Event {
1213+
vector<int> data = vector<int>(512);
1214+
}
1215+
list<shared_ptr<Event>> q;
1216+
1217+
void producer() {
1218+
for (int n = 0; n!=LOTS; ++n) {
1219+
lock_guard lk {m}; // 互斥信号量
1220+
q.push_back(make_shared<Event>());
1221+
cv.notify_one(); // 条件变量
1222+
}
1223+
}
1224+
```
1225+
1226+
> 从逻辑上说,这样应该工作得很好。具备清晰的逻辑,代码也健壮、可维护。不幸的是,这会导致大量的内存碎片。当16个生产者与4个消费者处理了10万个事件后,6GB以上的内存被碎片吞噬。
1227+
1228+
*经典碎片内存,所以对于长时间运行的系统,还是不能随便 new delete啊。*
1229+
1230+
> 解决碎片问题的传统方案是使用内存池分配器重写代码。内存池分配器用来管理固定尺寸空间分配,并且一次性分配大量的对象,而不是每次申请单独分配。幸运的是,C++直接支持这个功能。内存池分配器定义在std命名空间的pmr(多态内存资源)子空间中:
1231+
1232+
```cpp
1233+
pmr::synchronized_pool_resource pool;
1234+
1235+
struct Event {
1236+
vector<int> data = vector<int>{512, &pool};
1237+
}
1238+
1239+
list<shared_ptr<Event>> q {&pool};
1240+
1241+
void producer() {
1242+
for (int n = 0; n!=LOTS; ++n) {
1243+
lock_guard lk {m}; // 互斥信号量
1244+
q.push_back(allocate_shared<Event,pmr::polymorphic_allocator<Event>> {&pool});
1245+
cv.notify_one(); // 条件变量
1246+
}
1247+
}
1248+
```
1249+
1250+
*太深奥了,感觉得看多态内存相关补一下了后面。*
1251+
1252+
多态资源必须从 memory_resource 派生,并且定义了成员函数 allocate()、deallocate() 和 is_equal().
1253+
1254+
### 容器概述
1255+
1256+
| 容器名 | 内容 |
1257+
| ------------------------- | ----------------- |
1258+
| `vector<T>` | 可变尺寸数组 |
1259+
| `list<T>` | 双向链表 |
1260+
| `forward_list<T>` | 单向链表 |
1261+
| `deque<T>` | 双端队列 |
1262+
| `map<K,V>` | 关联数组 |
1263+
| `multimap<K,V>` | 关键字可重复的 map |
1264+
| `unordered_map<K,V>` | 哈希查找实现的 map |
1265+
| `unordered_multimap<K,V>` | 哈希版本的多值 map |
1266+
| `set<T>` | 只有关键字没有值的 map(集合) |
1267+
| `multiset<T>` | 值可以出现多次的集合 |
1268+
| `unordered_set<T>` | 哈希版本的集合 |
1269+
| `unordered_multiset<T>` | 哈希版本的多值集合 |
1270+
标准库还提供了容器适配器比如说 `queue<T>` `stack<T>` `priority_queue<T>`. 还有定长数组 `array<T,N>` 和 `bitset<N>`.
1271+
1272+
*草了,好多细节。*
1273+
1274+
注意一下 emplace_back 和 push_back 区别吧。
1275+
1276+
> emplace操作(比如emplace_back())获取元素构造函数的参数,并在容器中新分配的空间中直接构建对象,而不是将对象拷贝到容器中。例如,对于vector<pair<int,string>>类型,我们可以这样写:
1277+
1278+
```cpp
1279+
v.push_back(pair{1,"copy or move"}); // 赋值或者移动构造
1280+
v.emplace_back(1, "build in place"); // 就地构造
1281+
```
1282+
1283+
不过上面这种简单情况,优化器会优化成同等性能。
1284+
1285+
### 建议
1286+
1287+
- 标准库容器定义一个序列
1288+
-
1289+
1290+
#todo

0 commit comments

Comments
 (0)