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>
11
namespace std
{ using ::set_new_handler; using ::new_handler; }
12
#else
13
#include <new>
14
#endif
15
16
17
void* 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
34
void deallocate(void* ptr)
{
35
printf("deallocate %p\n",ptr);
36
free(ptr);
37
}
38
39
void* operator new(size_t size)
{ return allocate(size); }
40
void* operator new[](size_t size)
{ return allocate(size); }
41
void operator delete(void* ptr)
{ deallocate(ptr); }
42
43
class C
{
44
static int count;
45
public:
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
};
65
bool C::fail;
66
int C::count;
67
68
struct 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
};
79
bool S::fail;
80
81
void 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
90
void 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
98
int 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》。
回复 更多评论