我正在尝试用我自己的分配器替换新/删除。所以,重写安置新的和删除-对此相当满意。看起来像这样..。
void* operator new( size_t size, Allocator* a )
{
return a->Alloc( size );
}
template<class T> inline void MyDelete( T* p, Allocator* a )
{
if( p )
{
p->~T();
a->Free( p );
}
}C++语言指定,要删除位置,必须显式调用~dtor。编译器不为你做这件事。无论这是一个临时操作符,删除还是显式函数,如所示。
请参阅http://www2.research.att.com/~bs/bs_faq2.html#placement-delete
问题是-我如何才能让它在数组delete[]中工作呢?我知道我需要遍历数组并亲自调用~dtor。因此我需要数组的大小,
为澄清而编辑
我可以存储这些信息,也可以根据块大小推断它。但是,问题是,如果我分配一个带有析构函数的对象数组,而不是没有析构函数的对象数组,那么编译器(MSVC v9)会执行不同的操作,也就是说,如果有一个dtor,它将额外分配4个字节。这是因为标准delete[]的编译器需要做同样的事情,并且可以为delete[]配对适当的代码。
但是,在我自己的“放置”delete[]中,我无法知道编译器做了什么,也无法在编译时确定类是否有dtor。
例如。
char buf[ 1000 ];
MyClass* pA = new( buf ) MyClass[ 5 ];这里pA的值为buf + 4,如果存在~MyClass(),并且分配的内存量是相当大的(MyClass)* 5 +4。但是如果没有dtor,则pA == buf和分配的内存量是相当大的(MyClass)*5。
所以我的问题是--这种行为是一种语言标准,在编译器之间是一致的,还是MSVC特有的?还有人能很好地解决这个问题吗?我想唯一的选择是不使用new[],自己做构造,这很好,但是调用代码语法有点不寻常。或者强迫每个类都有一个析构函数。
发布于 2009-11-28 17:50:09
简短回答:
对这种使用没有直接的支持。如果您用不同的签名重载新的,编译器会认为它是一个新的过载(而不是放置新的),并添加了自己的记账代码。没有办法(我可以找到)对编译器说“解除您的簿记,并调用我的删除重载匹配此签名”-它只会插入代码,以解除簿记时,调用void operator delete(void* p)或void operator delete[](void* p)。
如果您确实使用新签名覆盖了new,编译器希望您在发生异常时定义带有匹配签名的delete --这是唯一一次使用delete。
没有位置删除在不可调用的意义上,但它是在异常情况下定义的(不做任何事情)。
长答案:
这个话题提出了一些有趣的观点:
void* operator new[](size_t sz, Allocator* a)过载到底是什么?void operator delete[](void* p, Allocator* a),就像编译器插入其簿记的void* operator new[](size_t sz, Allocator* a)一样。第一点:很多关于过载安置的讨论都是新的。考虑到编译器正在插入簿记保存代码,必须认为void* operator new[](size_t sz, Allocator* a)声明了(不放置)新的过载。它永远不会插入记账代码的安置新,因为安置新的重点是你正在处理它你自己。
第2点: R.E.“没有位置删除之类的东西”,你会发现一些看起来非常像它的东西(并且是这样的),比如VS2k8新的标题。它只是一个存根,用于在新放置期间出现异常的情况。但是,确实不能以一种有意义的方式调用placement。
第三点:如果有办法,我就找不到。这是问题的核心。
就解决这一问题的实际办法而言,这似乎是一次失败。
例如:
//intention: user provides memory pool, compiler works out how many bytes required
//and does its own book-keeping, as it would for a void* operator new[](size_t sz) overload
//calling syntax: CObj* pMyArr = new(pMyMemPool) CObj[20];
void* operator new[](size_t sz, IAlloc* pMemPool)
{ return pMemPool->alloc(sz); }
//problem: don't know the syntax to call this!
//e.g. delete[](pMyMemPool) pMyArr is syntax error
void* operator delete[](void* p, IAlloc* pMemPool)
{ return pMemPool->free(p); }
//nb: can be called as operator delete(pMyArr, pMyMemPool);
//but compiler does not finish its book-keeping or call dtors for you in that case.请注意,对于非数组new & delete也存在这种不对称。然而,由于(经验性的)编译器没有额外的簿记,所以所有这些都可以正常工作。再说一遍,如果这是我不知道的标准。
void* operator new(size_t sz, IAlloc* pMemPool)
{ return pMemPool->alloc(sz); }
//don't know syntax to get this called by compiler!
void operator delete(void* p, IAlloc* pMemPool)
{ pMemPool->free(p); }
//is ok though, can work around
template<class T> void tdelete(void* p, IAlloc* pMemPool)
{
//no problems, p points straight at object
p->~T();
operator delete(p, pMemPool);
//OR just
pMemPool->free(p);
}
void* operator new[](size_t sz, IAlloc* pMemPool)
{ return pMemPool->alloc(sz); }
//again, don't know syntax to end up here.
void operator delete[](void* p, IAlloc* pMemPool)
{ pMemPool->free(p); }
//can't work around this time!
template<class T> void tarrdelete(void* p, IAlloc* pMemPool)
{
//problem 1: how many to dtor?
for(int i=0; i<???; ++i)
{ reinterpret_cast<T*>(p+i)->~T(); }
//problem 2: p points at first element in array. this is not always the address
//that was allocated originally.
pMemPool->free(?);
//as already explained by OP, no way to tell if p is address allocated or
//address allocated+4 bytes, or something else altogether. this means no way to know what address to un-alloc or how many dtors to call.
}最后,我将陈述这些概况介绍。-没有扩展参数列表的重载确实有效:
//sz may include extra for book-keeping
void* operator new[](size_t sz)
{ return GAlloc->alloc(sz); }
//works fine, compiler handled book-keeping and p is the pointer you allocated
void operator delete[](void* p)
{ return GAlloc->free(p); }汇总:是否有允许使用扩展参数列表调用delete重载的语法,并启用编译器“魔术”。或者,是否有一种方法可以通过重写添加参数来放置新的位置?
怀疑回答:不。
推论:你不能完全自由地偏离6种内置的新签名。这样做会导致过载的新,编译器生成簿记,但没有访问相应的删除,以展开簿记。
警告:您可以偏离内置签名,但只需要注入您不需要在删除时再次处理的代码(例如插装)。如果您使用void* operator new(size_t s)版本进行分配,那么delete仍然可以正常工作。
(一些事实陈述来自调试器中的实验,可能只适用于MSVC8 (cl9) )。OP坐在我旁边的桌子上。)
发布于 2009-11-27 17:06:05
当有疑问时,请到专家那里:
http://www.stroustrup.com/bs_faq2.html#placement-delete
,但是我们以后如何正确地删除这些对象呢?没有内置的“安置删除”来匹配安置新的原因是没有一般的方法来保证它将被正确地使用。C++类型系统中没有任何东西允许我们推断p1指向在a1中分配的对象。指向任何分配到任何地方的X的指针都可以分配给p1。
该链接的其余部分将描述如何纠正这种情况。
发布于 2009-11-27 18:25:30
没有诸如“安置删除”这样的术语。正如您已经说过的,如果您分配了一些新的位置,那么当需要释放时,您需要手动调用析构函数,然后还需要处理分配给新位置的实际内存缓冲区。
但是,如果不手动跟踪您自己的分配大小,您要做的事情是不可能的。原因是“放置新”的全部目的是将分配与对象初始化分离开来。因此,在布局新的情况下,分配内存缓冲区的行为完全不同于构造或销毁任何对象可能(或不可能)生活在该缓冲区中的对象。
因此,例如,如果您分配了一些缓冲区,比如char buf[1000],然后使用新位置来在该缓冲区中构造一个Foo对象数组,那么C++应该在哪里存储数组大小信息?它不会将它存储在您的缓冲区中,因为它不知道您想使用该缓冲区做什么。因此,这取决于您记录每个分配的大小,然后适当地将其与取消分配相结合。
https://stackoverflow.com/questions/1809705
复制相似问题