全局“放置"删除[] [英] Global "placement" delete[]

查看:43
本文介绍了全局“放置"删除[]的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试用自己的分配器替换new/delete.因此,覆盖新的和删除的展示位置-对此感到非常满意.看起来像这样...

I am trying to replace new/delete with my own allocator(s). So, overriding placement new and delete - quite happy with that. Looks something like this ...

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.编译器不会为您执行此操作.如图所示,这是模板运算符删除还是显式函数.

The C++ language specifies that, for placement delete, you have to explicitly call the ~dtor. The compiler doesn't do it for you. Whether this is a templatised operator delete or explicit function as shown.

请参见 http://www2.research.att.com/〜bs/bs_faq2.html#placement-delete

问题是-如何使它对数组delete []有效?我知道我需要遍历数组并自己致电〜dtor.因此,我需要数组的大小

The problem is - how can I get this to work for array delete[]? I know I need to iterate through the array and call ~dtor myself. Therefore I need the size of the array,

为清楚起见而编辑

我可以存储此信息或从块大小中推断出该信息.但是,问题是如果我分配一个带有析构函数的对象数组与不带有析构函数的对象数组相比,编译器(MSVC v9)会做不同的事情,即如果有一个dtor它将分配额外的4个字节.这是因为标准delete []的编译器需要做同样的事情,并且可以为delete []配对适当的代码.

I can store this information or infer it from the block size. However, the problem is the compiler (MSVC v9) does different things if I am allocating an array of objects with destructors compared to ones without, i.e. if there is a dtor it will allocate an extra 4 bytes. This is because the compiler for standard delete[] needs to do the same thing and can pair up the appropriate code for delete[].

但是,在我自己的位置" delete []中,我无法知道编译器做了什么,也无法在类编译时安全地确定类是否有dtor.

However in my own "placement" delete[] I have no way of knowing what the compiler did or determining safely at compile time if the class has a dtor.

例如

char buf[ 1000 ];

MyClass* pA = new( buf ) MyClass[ 5 ];

如果存在〜MyClass(),则pA的值为buf + 4,而分配的内存量为sizeof(MyClass)* 5 +4.但是,如果没有dtor,则pA == buf且pA的值为分配的内存为sizeof(MyClass)* 5.

Here the value of pA is buf + 4 if there exists ~MyClass() and the amount of memory allocated is sizeof(MyClass) * 5 + 4. However if there is no dtor then pA == buf and the amount of memory allocated is sizeof(MyClass) * 5.

所以我的问题是-这种行为是一种语言标准并且在编译器之间是一致的,还是MSVC特有的?还有其他人可以很好地解决这个问题吗?我猜唯一的选择是不使用new []自己进行构造,但这很好,但是调用代码的语法有点不寻常..或强制每个类都具有析构函数.

So my question is - is this behaviour a language standard and consistent across compilers or is it a peculiar to MSVC? Has anyone else got a good solution to this problem? I guess the only option is to not use new[] and do the construction myself which is fine but then the calling code syntax is a little unusual .. or force every class to have a destructor.

推荐答案

简短答案:

对此用法没有直接支持.如果使用不同的签名重载new,则编译器将其视为new(而不是new)重载,并添加自己的簿记代码.我无法找到对编译器说解开簿记,并调用与该签名匹配的删除超载"的方法-它只会在调用 void运算符delete时插入代码来解开簿记.(void * p) void运算符delete [](void * p).

There is no direct support for this usage. If you overload new with a different signature, the compiler considers it an overload of new (not placement new) and adds its own book-keeping code. There is no way (I can find) to say to the compiler "unwind your book-keeping, and call my delete overload matching this signature" - it will only insert code to unwind the book-keeping when calling void operator delete(void* p) or void operator delete[](void* p).

如果您确实使用新的签名覆盖了new,则编译器希望您定义一个具有匹配签名的删除,以防在new期间出现异常-这是唯一使用它的时间.

If you do override new with a new signature, the compiler likes you to define a delete with matching signature in case of exceptions during new - this is the only time it gets used.

没有可以删除的放置删除,但它是在发生异常的情况下定义的(不执行任何操作).

There is no placement delete in the sense that it is not callable, but it is defined in case of exceptions (to do nothing).

详细答案:

该主题提出了一些有趣的观点:

This topic raises some interesting points:

  1. void *运算符new [](size_t sz,Allocator * a)到底是什么?
  2. 是否存在展示位置删除".
  3. 一个人如何调用 void运算符delete [](void * p,Allocator * a),就像编译器插入其簿记定稿那样?
  1. What, exactly, does void* operator new[](size_t sz, Allocator* a) overload?
  2. Is there, or is there not, a "placement delete".
  3. How does one invoke void operator delete[](void* p, Allocator* a), in such a way as the compiler inserts its book-keeping finalization?

要点1:关于新的重载放置的讨论很多.假设编译器正在插入簿记代码,则必须认为 void *运算符new [](size_t sz,Allocator * a)声明了(非放置)new的重载.它永远不会为新的放置位置插入记账代码,因为新的放置位置是您自己处理.

Point 1: Much talk about overloading placement new. Given that the compiler is inserting book keeping code, it must be of the opinion that that void* operator new[](size_t sz, Allocator* a) declares an overload of (non-placement) new. It will never insert book-keeping code for placement new, because the point of placement new is you are handling it youself.

第2点:R.E.没有诸如删除展示位置之类的东西",您将在例如VS2k8新标头.它只是在新放置期间发生异常的情况下使用的存根.但是,确实不能以有意义的方式调用展示位置删除.

Point 2: R.E. "no such thing as placement delete", you will find something that looks awfully like it (and commented as such) in e.g. the VS2k8 new header. It is simply a stub used in cases where an exception occurs during placement new. It does however appear to be true that you cannot invoke placement delete in a meaningful way.

第3点:如果有办法,我找不到.这是问题的核心.

Point 3: If there is a way, I can't find it. This is the heart of the problem.

就该问题的实际解决方案而言,这似乎是一个失败.

In terms of a practical solution to the problem, it appears to be a bust.

例如:

//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&也删除.但是,由于(根据经验)所涉及的编译器没有额外的簿记功能,因此所有编译器都可以正常工作.再说一次,如果这是标准的我不知道.

Note that this asymmetry exists for non-array new & delete too. However, because (empirically) the compiler in question does no extra book-keeping it can all be made to work. Again, if this is enshrined in standard I don't know.

    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. 

    }

最后,我要说明一下观察值.-没有扩展参数列表的重载可以正常工作:

Finally, I'll state the obvs. - overloads without the extended parameter list do work:

//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的重载,并且启用了编译器魔术".或者,是否可以通过覆盖将参数添加到新的展示位置?

Summary: Is there syntax that will allow calls to an overload of delete with an extended parameter list, with compiler "magic" enabled. Or, is there a way to add parameters to placement new by override?

可能的答案:否.

推论:您不能完全自由地偏离6个内置新签名.这样做会导致新的重载,编译器会生成簿记,但无法访问相应的删除内容以展开簿记.

Corollary: You cannot stray from the 6 built-in new signatures with complete freedom. Doing so results in an overload of new, with compiler generated book-keeping, but no access to the corresponding delete to unwind to book-keeping.

注意事项:您可以偏离内置签名,而只是注入不需要在删除(例如,检测)时再次处理的代码.如果您使用分配给 void *运算符new(size_t s)的版本,则删除仍然可以正常进行.

Caveat: You can stray from the built-in signatures, but only to inject code you do not need to handle again at delete (e.g. instrumentation). If you fall through to the void* operator new(size_t s) version for the allocation, then delete will still work as normal.

(一些事实陈述来自调试器中的实验,可能仅适用于MSVC8(cl9).OP位于我的下一张桌子上.)

(Some statements of fact are drawn from experiments in the debugger and may only apply to MSVC8 (cl9). OP sits on next desk to me.)

这篇关于全局“放置"删除[]的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆