Kaynağa Gözat

feat: 添加宏优化版本 以及 new_handler

liucong5 2 yıl önce
ebeveyn
işleme
461b68ae1f

+ 30 - 0
cpp/temp-cpp-proj/temp-cpp-proj/NewHandler.cpp

@@ -0,0 +1,30 @@
+#include <iostream>
+#include <new>
+
+using namespace std;
+
+void newErr(void)
+{
+	cout << "new error" << endl;
+	system("pause");
+	exit(-1);
+}
+
+void foo_new_handler() {
+
+	double* ptr[50];
+	set_new_handler(newErr);
+	for (int i = 0; i < 50; i++)
+	{
+		ptr[i] = new double[99999999]; //不断申请空间
+		cout << "ptr[" << i << "]" << "->success" << endl;
+	}
+
+	system("pause");
+}
+
+int main()
+{
+	foo_new_handler();
+	return 0;
+}

+ 16 - 12
cpp/temp-cpp-proj/temp-cpp-proj/src/CustomAllocator.cpp

@@ -50,23 +50,26 @@ void MyAllocator::deallocate(void* p, size_t)
 	freeStore = (obj*)p;
 }
 
+#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;
-	static MyAllocator myAlloc;
 
 public:
 	Goo(long l) : L(l) {}
-	static void* operator new(size_t size) {
-		return myAlloc.allocate(size);
-	}
 
-	static void operator delete(void* pDead, size_t size) {
-		myAlloc.deallocate(pDead, size);
-	}
+	DECLARE_POOL_ALOC()
 };
 
-MyAllocator Goo::myAlloc;
+IMPLEMENT_POOL_ALLOC(Goo);
 
 void foo_myALlocator() {
 	std::cout << sizeof(Goo) << std::endl;
@@ -87,7 +90,8 @@ void foo_myALlocator() {
 	}
 }
 
-int main() {
-	foo_myALlocator();
-	return 0;
-}
+//int main() {
+//	foo_myALlocator();
+//
+//	return 0;
+//}

+ 1 - 1
cpp/temp-cpp-proj/temp-cpp-proj/temp-cpp-proj.vcxproj

@@ -109,7 +109,6 @@
     <Link>
       <SubSystem>Console</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
-      <AdditionalOptions>/ALIGN:4 %(AdditionalOptions)</AdditionalOptions>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@@ -129,6 +128,7 @@
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
+    <ClCompile Include="NewHandler.cpp" />
     <ClCompile Include="src\Airplane.cpp" />
     <ClCompile Include="src\main.cpp" />
     <ClCompile Include="src\Screen.cpp" />

+ 3 - 0
cpp/temp-cpp-proj/temp-cpp-proj/temp-cpp-proj.vcxproj.filters

@@ -27,5 +27,8 @@
     <ClCompile Include="src\CustomAllocator.cpp">
       <Filter>源文件</Filter>
     </ClCompile>
+    <ClCompile Include="NewHandler.cpp">
+      <Filter>源文件</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 91 - 2
cpp/内存机制.md

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