1#include <stddef.h>
2#include <stdlib.h>
3#include <stdio.h>
4#include <vector>
5
6#include <stdexcept>
7
8
9#if defined(_MSC_VER) && _MSC_VER<1400
10#include <new.h>
11namespace std { using ::set_new_handler; using ::new_handler; }
12#else
13#include <new>
14#endif
15
16
17void* allocate(size_t size) {
18 if (size==0) size = 1;
19 for (void* p=0;;) {
20 p = malloc(size);
21 if (p) {
22 printf("allocate %p\n",p);
23 return p;
24 }
25 std::new_handler handler = std::set_new_handler(0);
26 std::set_new_handler(handler);
27 if (handler)
28 handler();
29 else
30 throw std::bad_alloc();
31 }
32}
33
34void deallocate(void* ptr) {
35 printf("deallocate %p\n",ptr);
36 free(ptr);
37}
38
39void* operator new(size_t size) { return allocate(size); }
40void* operator new[](size_t size) { return allocate(size); }
41void operator delete(void* ptr) { deallocate(ptr); }
42
43class C {
44 static int count;
45public:
46 static bool fail;
47 C() {
48 if (fail)
49 throw std::exception();
50 printf("C::C(),%d\n",++count);
51 }
52 ~C() {
53 printf("C::~C(),%d\n",count--);
54 }
55 C(const C& ) {
56 printf("C::(const C&),%d\n",++count);
57 }
58
59
60 //void* operator new(size_t,void* place) { return place; }
61 void* operator new(size_t size) { return allocate(size); }
62 void* operator new[](size_t size) { return allocate(size); }
63 void operator delete(void* ptr) { deallocate(ptr); }
64};
65bool C::fail;
66int C::count;
67
68struct S {
69 static bool fail;
70 S() {
71 if (fail)
72 throw std::exception();
73 printf("construct\n");
74 }
75 ~S() {
76 printf("destroy\n");
77 }
78};
79bool S::fail;
80
81void test_class(int dim) {
82 if (dim<=0)
83 return;
84 C::fail = dim==4;
85 C* arr = new C[dim];
86 delete[] arr;
87}
88
89
90void test_global(int dim) {
91 if (dim<=0)
92 return;
93 S::fail = dim==4;
94 S* arr = new S[dim];
95 delete[] arr;
96}
97
98int main() {
99 using namespace std;
100 int dim = 0;
101 for (printf("input dim: ");scanf("%d",&dim)==1;printf("input dim: "))
102 {
103 try {
104 test_class(dim);
105 }
106 catch (std::exception& ) {
107 printf(" ---- catch an exception ----\n");
108 }
109 try {
110 test_global(dim);
111 }
112 catch (std::exception& ) {
113 printf(" ---- catch an exception ----\n");
114 }
115 }
116}
117
有两个底层分配函数allocate和deallocate,它们使用malloc和free。
用这两个函数实现全局的3个op new,op new[], op delete,没有op delete[]
还用这两个函数实现了C的3个op new,op new[], op delete,同样没有op delete[]
用如下参数编译
cl /EHsc /MD /analyze /W3
你看看结果吧。
我用vc8、9测过(vc6不支持动态crt库,vc10我没装)。
反正两处delete[] arr;都没有调用 op delete。
它们调用那个全局的,没有被重写的op delete[]。
如果静态链接,该全局默认的op delete[]会被inline, 再调用该编译单元中定义的op delete。
如果动态链接,op delete[]不会被inline,会调用crt库中提供的op delete。
总之,这两处delete[] arr;都没有调用deallocate。
当然, 你可以说你只静态链接到crt库。
也可以说你的allocate和deallocate底层实现绝对会一直保持与vc提供的crt兼容。
但是,你的代码的用户了解么?
难道你打算在文档中写“使用我的库的代码者,使用的crt库必须满足XXX要求,必须自己测试YYY”,只是为了你自己可以少写一个 op delete[]?
这不是程序库开发者的态度。
还有两个小问题。
C* pc = static_cast<C*>(malloc(sizeof(*pc));
new (pc) C; // 编译错误
C* pc2 = new (std::nothrow) C; // 编译错误
当然, 你还是可以说你绝对不会用这种东西, 你是实用主义嘛。
但是你的库的使用者呢?
“出发点只是想找到一个经过验证的(大的、成功的产品使用过的)简便的工具”
你觉得这可以说明该产品中的每个细节都是无可挑剔的么?
越是大的产品,测试越不容易,更不容易暴露其中的问题,同时也许忽悠客户也更容易。
确实没有任何事物都是完美的,但不能连追求完美的心都舍弃了。
同时,从实用角度出发,让该库变得更完美,所付出的代价非常小,“按规则”办事就可以了,10来行代码的事,何乐而不为?
规则可以见《EffCpp》或者《C++CodingStandard》。
回复 更多评论