|
|
@@ -638,11 +638,13 @@ public:
|
|
|
private:
|
|
|
obj* freeStore = nullptr;
|
|
|
const int CHUNK = 5; // 标准库是20
|
|
|
+ const int min_size = 8;
|
|
|
};
|
|
|
|
|
|
void* MyAllocator::allocate(size_t size)
|
|
|
{
|
|
|
obj* p = nullptr;
|
|
|
+ size = size > min_size ? size : min_size;
|
|
|
if (!freeStore) {
|
|
|
size_t chunk = CHUNK * size;
|
|
|
freeStore = p = (obj*)malloc(chunk);
|
|
|
@@ -677,7 +679,7 @@ public:
|
|
|
static MyAllocator myAlloc;
|
|
|
|
|
|
public:
|
|
|
- Goo(long l): L(l) {}
|
|
|
+ Goo(long l) : L(l) {}
|
|
|
static void* operator new(size_t size) {
|
|
|
return myAlloc.allocate(size);
|
|
|
}
|
|
|
@@ -714,7 +716,13 @@ int main() {
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-> 上面的代码可能运行不了(与编译器版本有关),但是可以表达我们对 allocator 运行的机制
|
|
|
-
|
|
|
使用这种写法的 allocator 的内存分配器可以给其他任意类型的对象使用,对象无需再关注内存如何分配,而是仅仅需要引入 `MyAllocator` 并且在 `operator new` 和 `operator delete` 中使用即可
|
|
|
|
|
|
+这里需要注意的是自定义的 `MyAllocator` 有一个属性 `min_size = 8`,在 x64 的平台上指针占 **8个字节**,上述代码的 Goo 对象占 **4个字节**,也就是说在地址运算的时候会将前后两个 Goo 对象的内存合并为一个地址,最后出现错误
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+以上图为例,一格黑色方块就是一个 `Goo` 对象的大小4字节,是提前为未来的 `Goo` 创建出来的内存空间。理论上来说第一格内存地址为 `D0`,其 `next` 指针指向 `D4`,但是实际运行时由于地址为 **8字节**,所以第一格的 `next` 指针的值其实是 `D0D4`,而这个地址其实是一个未知地址,最后导致运行报错
|
|
|
+
|
|
|
+上述代码其实也很明显,将 `Goo` 的内存块 `cast` 成 `Obj` 对象来管理空闲空间,以此达到节省内存空间的效果。当内存块需要被使用时,其会从空闲链表中移出并设置,此时就与链表无关的,所以也不用管 `next` 的值。等到 Goo 被 delete 时,其会从 Goo 再次转为 `Obj` 链表节点,这时也不用管 `Goo` 对象的值是多少,因为此时 Goo 对象已经无效了
|
|
|
+
|