|
|
@@ -716,8 +716,6 @@ int main() {
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-使用这种写法的 allocator 的内存分配器可以给其他任意类型的对象使用,对象无需再关注内存如何分配,而是仅仅需要引入 `MyAllocator` 并且在 `operator new` 和 `operator delete` 中使用即可
|
|
|
-
|
|
|
这里需要注意的是自定义的 `MyAllocator` 有一个属性 `min_size = 8`,在 x64 的平台上指针占 **8个字节**,上述代码的 Goo 对象占 **4个字节**,也就是说在地址运算的时候会将前后两个 Goo 对象的内存合并为一个地址,最后出现错误
|
|
|
|
|
|

|
|
|
@@ -726,3 +724,94 @@ int main() {
|
|
|
|
|
|
上述代码其实也很明显,将 `Goo` 的内存块 `cast` 成 `Obj` 对象来管理空闲空间,以此达到节省内存空间的效果。当内存块需要被使用时,其会从空闲链表中移出并设置,此时就与链表无关的,所以也不用管 `next` 的值。等到 Goo 被 delete 时,其会从 Goo 再次转为 `Obj` 链表节点,这时也不用管 `Goo` 对象的值是多少,因为此时 Goo 对象已经无效了
|
|
|
|
|
|
+通过上面的 `MyAllocator` 的内存分配器可以给其他任意类型的对象使用,对象无需再关注内存如何分配,而是仅仅需要引入 `MyAllocator` 并且在 `operator new` 和 `operator delete` 中使用即可
|
|
|
+
|
|
|
+然后稍微使用宏来优化一下
|
|
|
+
|
|
|
+```cpp
|
|
|
+#define DECLARE_POOL_ALOC() \
|
|
|
+public: \
|
|
|
+ static void* operator new(size_t size) { return myAlloc.allocate(size); } \
|
|
|
+ static void operator delete(void* pDead, size_t size) { myAlloc.deallocate(pDead, size); } \
|
|
|
+protected:\
|
|
|
+ static MyAllocator myAlloc;
|
|
|
+
|
|
|
+#define IMPLEMENT_POOL_ALLOC(class_name) MyAllocator class_name::myAlloc;
|
|
|
+
|
|
|
+class Goo {
|
|
|
+public:
|
|
|
+ long L;
|
|
|
+
|
|
|
+public:
|
|
|
+ Goo(long l) : L(l) {}
|
|
|
+
|
|
|
+ DECLARE_POOL_ALOC()
|
|
|
+};
|
|
|
+
|
|
|
+IMPLEMENT_POOL_ALLOC(Goo);
|
|
|
+```
|
|
|
+
|
|
|
+如此一来,对于所有的类来说仅仅使用**两个宏**就可以实现内存池的功能
|
|
|
+
|
|
|
+### new handler
|
|
|
+
|
|
|
+当 operator new 无法给你分配出所需的 memory 时,会抛出 `std::bad_alloc` `exception` 异常,某些旧的编译器则是返回0
|
|
|
+
|
|
|
+当然你也可以指定不要抛出异常
|
|
|
+
|
|
|
+```cpp
|
|
|
+new(std::nothrow) int;
|
|
|
+```
|
|
|
+
|
|
|
+抛出异常之前会先(不止一次)调用一个可由 `client` 指定的 `handler`,下面的带啊吗是 new handler 的形式和设定方法
|
|
|
+
|
|
|
+```cpp
|
|
|
+typedef void (*new_handler)();
|
|
|
+new_handler set_new_handler(new_handler p) thro();
|
|
|
+```
|
|
|
+
|
|
|
+实际操作如下
|
|
|
+
|
|
|
+```cpp
|
|
|
+#include <new>
|
|
|
+
|
|
|
+void newErr(void)
|
|
|
+{
|
|
|
+ cout << "new error" << endl;
|
|
|
+ system("pause");
|
|
|
+ exit(-1);
|
|
|
+}
|
|
|
+
|
|
|
+set_new_handler(newErr);
|
|
|
+```
|
|
|
+
|
|
|
+根据 `operator new` C++ 源码来看,在 `malloc` 申请内存失败之后,会调用 `_callnewh` 函数,该函数就是 `call new hanlder` 的缩写
|
|
|
+
|
|
|
+这样做其实就是让用户判断一下有没有补救措施,然后再调用一次 `malloc` ,说不定就成功了
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+```cpp
|
|
|
+void* __CRTDECL operator new(size_t const size)
|
|
|
+{
|
|
|
+ for (;;)
|
|
|
+ {
|
|
|
+ if (void* const block = malloc(size))
|
|
|
+ {
|
|
|
+ return block;
|
|
|
+ }
|
|
|
+ if (_callnewh(size) == 0)
|
|
|
+ {
|
|
|
+ if (size == SIZE_MAX)
|
|
|
+ {
|
|
|
+ __scrt_throw_std_bad_array_new_length();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ __scrt_throw_std_bad_alloc();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // The new handler was successful; try to allocate again...
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|