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》。
回復 更多評論