分析一下

anrxhzh 2002-04-27 07:50:10
#include <iostream>
using namespace std;

class test{
public:
test(){ cout<<"test()\n"; };
~test(){ cout<<"~test()\n"; };
void* operator new[] (size_t);
void* operator new[] (size_t,int);
void operator delete[] (void*,size_t);
};

void* test::operator new[] (size_t s)
{
cout << "new[" << s << "]";
void* p=::operator new(s);
cout << "=" << p << endl;
return p;
}

void* test::operator new[] (size_t s,int n)
{
cout << "new[" << s << "," << n << "]" ;
return 0;
}

void test::operator delete[] (void* p,size_t s)
{
cout << "delete[" << p << "," << s << "]\n";
::operator delete(p);
}

int main()
{
test* p = new test[3];
delete[] p;

cout<<endl;

p = new test[0];
delete[] p;

cout<<endl;

p = new(0) test[3];
delete[] p;

cout<<endl;

p = new(0) test[0];
delete[] p;

cout<<endl;
}
//output:
//new[7]=00481840
//test()
//test()
//test()
//~test()
//~test()
//~test()
//delete[00481840,1]
//
//new[4]=00481850
//delete[00481850,1]
//
//new[7,0]
//new[4,0]
...全文
31 点赞 收藏 14
写回复
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
anrxhzh 2002-07-14
完全赞同 prototype(原型) ,C++ 中只有placement new,没有placement delete,其实这个帖子自始至终讨论的都是placement allocation 和placement deallocation 。我在前面有很多误解,不过有一点是正确的,这里存在二义性,现在看来这个二义性是由 void* operator new(size_t,size_t) 引进的,所以我要修正一下我的建议,应该是:不要重载void* operator new(size_t,size_t)
回复
prototype 2002-07-14
en. you are right, anrxhzh(百宝箱). that is really an ambiguity.
also, make a correction for my first post in this thread:
i think zoukh(无名) is correct in her statement:

> If an exception is thrown from ctor, the language will assure
> that correct placement delete will be called. That's why
> placement new/delete should be in pairs.

after carefully reading, i know she really meant placement deallocation function by 'placement delete', while i thought that was someting like 'delete( ... ) [] p;' (as a couter-part to the placement new expression). sorry about my carelessness.
回复
anrxhzh 2002-07-13
operator delete(void*, size_t)

this same operator delete overload serves two distinct roles:

A non-placement deallocation function matching every allocation function and called via delete x. The size_t parameter is the size of the allocated space.

A placement deallocation function matching operator new(size_t, size_t) and called via X constructor throw. The size_t parameter is the original placement argument n to operator new.

(Interesting implication: operator delete(void *, size_t) does not inherently know how it was called, or how to interpret its extra parameter. This is the only deallocation function to suffer these twin ambiguities [4].)

上文摘自: http://www.cuj.com/articles/2001/0104/0104e/0104e.htm?topic=articles

这里的确存在不可辨别的二义性,下面是我在gcc下做的测试。

#include <iostream>
using namespace std;

class test{
public:
test(){ cout<<"test()\n"; throw 0;};
~test(){ cout<<"~test()\n"; };

void* operator new(size_t);
void* operator new(size_t,size_t);
void operator delete(void*,size_t);

void* operator new[](size_t);
void* operator new[](size_t,size_t);
void operator delete[](void*,size_t);
};

void* test::operator new (size_t s)
{
cout << "operator new(size_t)" << s ;
void* p=::operator new(s);
cout << "=" << p << endl;
return p;
}

void* test::operator new (size_t s, size_t placement)
{
cout << "operator new(size_t,size_t)" << s << "," << placement ;
void* p=::operator new(s);
cout << "=" << p << endl;
return p;
}

void test::operator delete(void* p, size_t s)
{
cout << "operator delete(void*,size_t)" << p << "," << s << endl;
::operator delete(p);
}

void* test::operator new[] (size_t s)
{
cout << "operator new[](size_t)" << s ;
void* p=::operator new(s);
cout << "=" << p << endl;
return p;
}

void* test::operator new[] (size_t s, size_t placement)
{
cout << "operator new[](size_t,size_t)" << s << "," << placement ;
void* p=::operator new(s);
cout << "=" << p << endl;
return p;
}

void test::operator delete[](void* p, size_t s)
{
cout << "operator delete[](void*,size_t)" << p << "," << s << endl;
::operator delete(p);
}

int main()
{
try {
cout << "placement test...\n";
test* p = new(999999999) test;
delete p;
}
catch(...) {
}
cout << endl;
try {
cout << "normal test...\n";
test* p = new test;
delete p;
}
catch(...) {
}
cout << endl;
try {
cout << "placement array test...\n";
test* p = new(999999999) test[3];
delete p;
}
catch(...) {
}
cout << endl;
try {
cout << "normal test...\n";
test* p = new test[3];
delete p;
}
catch(...) {
}

cout<<endl;
}

//output:

//placement test...
//operator new(size_t,size_t)1,999999999=0xa010730
//test()
//operator delete(void*,size_t)0xa010730,999999999

//normal test...
//operator new(size_t)1=0xa010730
//test()
//operator delete(void*,size_t)0xa010730,1

//placement array test...
//operator new[](size_t,size_t)11,999999999=0xa010730
//test()
//operator delete[](void*,size_t)0xa010730,999999999

//normal test...
//operator new[](size_t)11=0xa010730
//test()
//operator delete[](void*,size_t)0xa010730,11
回复
prototype 2002-07-13
> > 没有调用delete的原因似乎是delete的是空指针,让我们来验证一下:
> no, 'delete' is called. what is not called is dtor.
sorry, this is not clear. let me re-state it: delete operator (not your overloaded one) will first examine your pointer to see if it is null. if not, your deallocation function, i.e. your overloaded 'operator delete[]', will be called; otherwise it does nothing. (note: 2 things here, one is delete operator, the other is deallocation function.)
so you are right. your overloaded 'delete[]' is not called due to the null pointer.
回复
prototype 2002-07-13
> 为什么是1,即sizeof(test)呢,应该是7才对吧。
yes.

> void* operator new[] (size_t,int); 有两重身份,第一种是普通的数组释
> 放操作符,int的含义是释放内存的字节数;第二种是占位数组释放操作符,
that is 'size_t', not 'int'.

> int的含义是占位参数。
this is correct.

> 没有调用delete的原因似乎是delete的是空指针,让我们来验证一下:
no, 'delete' is called. what is not called is dtor.

> 不要重载 void operator delete[] (void*,size_t)
that is ok (surely unless your compiler is crappy :-).

> 原因一:存在二义性,前面讲过了,int的含义第一种是释放内存的字节
> 数;第二种是占位参数。不仅存在二义性,而且这种二义性是无法区分的。
not ambiguous. see above.


> 原因三:编译器的bug,前面已经讲得很清楚了。这里还有另一个bug
correct.

> new(2, f) T(5) ->operator new(sizeof(T) * 5 + padding2, 2, f)
you meant 'new(2, f) T[5]'? ;-)

> If an exception is thrown from ctor, the language will assure
> that correct placement delete will be called. That's why
> placement new/delete should be in pairs.
that is not right, amigo. there is simply no 'placement delete' in c++.

回复
anrxhzh 2002-04-30
TCPL 15.6.1 Array Allocation 中的示例代码:
class Employee{
public:
void* operator new[](size_t);
void* operator delete[](void*,size_t);
//...
};
void f(int s)
{
Employee* p = new Employee[s];
//...
delete[] p;
}

我强调的是void* operator delete[](void*,size_t)具有二义性,所以不要重载它,这样会带来一个副作用,那就是也不能重载void* operator new[](size_t,int),也就是说禁止了 new(int) T[s]。

参考文献:
1997 C++ Public Review Document
3.7.3.2 Deallocation functions [basic.stc.dynamic.deallocation]

1 Deallocation functions shall be class member functions or global func-
tions; a program is ill-formed if deallocation functions are declared
in a namespace scope other than global scope or declared static in
global scope.

2 Each deallocation function shall return void and its first parameter
shall be void*. A deallocation function can have more than one param-
eter. If a class T has a member deallocation function named operator
delete with exactly one parameter, then that function is a usual (non-
placement) deallocation function. If class T does not declare such an
operator delete but does declare a member deallocation function named
operator delete with exactly two parameters, the second of which has
type std::size_t (_lib.support.types_), then this function is a usual
_________________________
8) The intent is to have operator new() implementable by calling mal-
loc() or calloc(), so the rules are substantially the same. C++ dif-
fers from C in requiring a zero request to return a non-null pointer.

deallocation function. Similarly, if a class T has a member dealloca-
tion function named operator delete[] with exactly one parameter, then
that function is a usual (non-placement) deallocation function. If
class T does not declare such an operator delete[] but does declare a
member deallocation function named operator delete[] with exactly two
parameters, the second of which has type std::size_t, then this func-
tion is a usual deallocation function. A deallocation function can be
an instance of a function template. Neither the first parameter nor
the return type shall depend on a template parameter. [Note: that is,
a deallocation function template shall have a first parameter of type
void* and a return type of void (as specified above). ] A dealloca-
tion function template shall have two or more function parameters. A
template instance is never a usual deallocation function, regardless
of its signature.

3 The value of the first parameter supplied to a deallocation function
shall be a null pointer value, or refer to storage allocated by the
corresponding allocation function (even if that allocation function
was called with a zero argument). If the value of the first argument
is a null pointer value, the call to the deallocation function has no
effect. If the value of the first argument refers to a pointer
already deallocated, the effect is undefined.

4 If the argument given to a deallocation function is a pointer that is
not the null pointer value (_conv.ptr_), the deallocation function
will deallocate the storage referenced by the pointer thus rendering
the pointer invalid. At the time storage is deallocated, the values
of any pointers that refer to that deallocated storage become indeter-
minate. The effect of using the value of a pointer with an indetermi-
nate value is undefined.9)
回复
anrxhzh 2002-04-30
我用微软拼音法查找了一下bj的词组,能够跟当前语境匹配的大约有:
颁奖
不解
笔记
标记
蹩脚
如果是英语的话,我只能想到big job

哪位老大能告诉我bj到底是什么东东?


回复
zoukh 2002-04-29
The new syntax:
assume 2, 5: int

new T ->operator new(sizeof(T)) //should have operator delete(void)

new T[5] ->operator new[](sizeof(T) * 5 + padding1) // should have operator delete[](void*)

new(2, f) T ->operator new(sizeof(T), 2, f) // placement new, should have operator

delete[](void*, int, f_type)

new(2, f) T(5) ->operator new(sizeof(T) * 5 + padding2, 2, f)

If calling to operator new returns non-NULL, constructor will be called.

If an exception is thrown from ctor, the language will assure that correct placement delete will

be called. That's why placement new/delete should be in pairs.

class test{
public:
test(){ cout<<"test()\n"; };
~test(){ cout<<"~test()\n"; };
void* operator new[] (size_t);
void* operator new[] (size_t,int); //== should have
//== void operator delete[](void*, int); to accompany it
void operator delete[] (void*,size_t); //== should be void operator delete[] (void*);
};



...
p = new test[0]; // new(padding)...
delete[] p; //delete[](void*)

cout<<endl;

p = new(0) test[3]; // new(size_t, 0); no ctor is called as return value is NULL
delete[] p; // should explicit call test::operator delete(p, int());

cout<<endl;

p = new(0) test[0]; // new(padding, 0)
delete[] p;
...

So:
following the conventions when provide your own new/delete, and make sure placement new/delete

are in pair.
回复
anrxhzh 2002-04-28
疑问一:
new[7]=00420080
test()
test()
test()
~test()
~test()
~test()
delete[00420080,1]

为什么是1,即sizeof(test)呢,应该是7才对吧。
回复
anrxhzh 2002-04-28
好了,疑问都清除了,可以下结论了:不要重载 void operator delete[] (void*,size_t)

原因一:存在二义性,前面讲过了,int的含义第一种是释放内存的字节数;第二种是占位参数。不仅存在二义性,而且这种二义性是无法区分的。

原因二:如果你期望int的含义第一种是释放内存的字节数,而且你的运气好,没有二义性存在,那末你为什么关心释放内存的字节数呢?可以想象的原因是你期望能够计算出将要delete的对象的个数,遗憾的是这是不看能的,因为不同编译器的extra字节数不同。

原因三:编译器的bug,前面已经讲得很清楚了。这里还有另一个bug http://www.csdn.net/expert/topic/673/673512.xml?temp=.9795801

总结一下:如果你真的打算使用重载new/delete,new[]/delete[]的方法来定制内存分配策略,那么一切只能依靠自己,不要打算利用C++提供的extra机制。
回复
anrxhzh 2002-04-28
最新的测试结果:
VisualAge C++ Professional / C for AIX Compiler, Version 5
很有趣,看来是VC的bug,而且不同编译器的extra字节数也不同。
希望有条件的朋友能给出其他平台/编译器的测试结果。

//output:
//sizeof(test) is 1
//new[11]=200095c8
//0,0,0,3,16,16,190,239,32,67,32,delete[200095c8,11]
//new[17]=200095c8
//0,0,0,9,16,16,190,239,32,67,32,67,0,0,0,0,0,delete[200095c8,17]

//output:(如果去掉注释 comment1)
//sizeof(test) is 1
//new[11]=20009678
//0,0,0,3,16,16,190,239,32,67,32,delete[20009678,11]
//new[17]=20009678
//0,0,0,9,16,16,190,239,32,67,32,67,0,0,0,0,0,delete[20009678,17]
回复
挺拔的劲松 2002-04-28
gz
回复
anrxhzh 2002-04-28
//疑问2:难道又跳进微软的BUG了?
#include <iostream>
#include <algorithm>
#include <iterator>
using namespace std;

class test{
public:
//~test(){}; //comment1
void* operator new[] (size_t);
void operator delete[] (void*,size_t);
private:
static void* p_;
static size_t s_;
public:
static void set(void* p,size_t s){p_=p;s_=s;}
static void display(){copy(static_cast<char*>(p_),static_cast<char*>(p_)+s_,ostream_iterator<int>(cout,","));}
};
void* test::p_ = 0;
size_t test::s_ = 0;

void* test::operator new[] (size_t s)
{
cout << "new[" << s << "]";
void* p=::operator new(s);
cout << "=" << p << endl;
test::set(p,s);
return p;
}

void test::operator delete[] (void* p,size_t s)
{
cout << "delete[" << p << "," << s << "]\n";
::operator delete(p);
}

int main()
{
cout << "sizeof(test) is " << sizeof(test) << endl;
test* p = new test[3];
test::display();
delete[] p;

p = new test[9];
test::display();
delete[] p;
cout<<endl;
}
//output:
//sizeof(test) is 1
//new[3]=00420080
//0,67,0,delete[00420080,1]
//new[9]=00420070
//-97,-76,64,0,-52,-76,64,0,80,delete[00420070,1]

//output:(如果去掉注释 comment1)
//sizeof(test) is 1
//new[7]=00420080
//3,0,0,0,-126,-73,64,delete[00420080,1]
//new[13]=00420070
//9,0,0,0,-75,-74,64,0,80,0,0,0,17,delete[00420070,1]
回复
anrxhzh 2002-04-27
p = new(0) test[3];//没有匹配的delete,为什么不写呢

void* operator new[] (size_t,int); 有两重身份,第一种是普通的数组释放操作符,int的含义是释放内存的字节数;第二种是占位数组释放操作符,int的含义是占位参数。
没有调用delete的原因似乎是delete的是空指针,让我们来验证一下:

重新定义test::operator new[] (size_t,int)如下
void* test::operator new[] (size_t s,int n)
{
cout << "new[" << s << "," << n << "]" ;
return operator new[](s);
}

输出:
new[7]=00420080
test()
test()
test()
~test()
~test()
~test()
delete[00420080,1]

new[4]=00420080
delete[00420080,1]

new[7,0]new[7]=00420080
test()
test()
test()
~test()
~test()
~test()
delete[00420080,1]

new[4,0]new[4]=00420080
delete[00420080,1]
回复
发动态
发帖子
C语言
创建于2007-09-28

6.3w+

社区成员

C语言相关问题讨论
申请成为版主
社区公告
暂无公告