首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >全球“布局”delete[]

全球“布局”delete[]
EN

Stack Overflow用户
提问于 2009-11-27 16:41:11
回答 4查看 3.3K关注 0票数 9

我正在尝试用我自己的分配器替换新/删除。所以,重写安置新的和删除-对此相当满意。看起来像这样..。

代码语言:javascript
复制
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。

例如。

代码语言:javascript
复制
char buf[ 1000 ];

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

这里pA的值为buf + 4,如果存在~MyClass(),并且分配的内存量是相当大的(MyClass)* 5 +4。但是如果没有dtor,则pA == buf和分配的内存量是相当大的(MyClass)*5。

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

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2009-11-28 17:50:09

简短回答:

对这种使用没有直接的支持。如果您用不同的签名重载新的,编译器会认为它是一个新的过载(而不是放置新的),并添加了自己的记账代码。没有办法(我可以找到)对编译器说“解除您的簿记,并调用我的删除重载匹配此签名”-它只会插入代码,以解除簿记时,调用void operator delete(void* p)void operator delete[](void* p)

如果您确实使用新签名覆盖了new,编译器希望您在发生异常时定义带有匹配签名的delete --这是唯一一次使用delete。

没有位置删除在不可调用的意义上,但它是在异常情况下定义的(不做任何事情)。

长答案:

这个话题提出了一些有趣的观点:

  1. void* operator new[](size_t sz, Allocator* a)过载到底是什么?
  2. 是否存在“放置删除”?
  3. 如何调用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。

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

就解决这一问题的实际办法而言,这似乎是一次失败。

例如:

代码语言:javascript
复制
//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也存在这种不对称。然而,由于(经验性的)编译器没有额外的簿记,所以所有这些都可以正常工作。再说一遍,如果这是我不知道的标准。

代码语言:javascript
复制
    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. 

    }

最后,我将陈述这些概况介绍。-没有扩展参数列表的重载确实有效:

代码语言:javascript
复制
//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坐在我旁边的桌子上。)

票数 3
EN

Stack Overflow用户

发布于 2009-11-27 17:06:05

当有疑问时,请到专家那里:

http://www.stroustrup.com/bs_faq2.html#placement-delete

,但是我们以后如何正确地删除这些对象呢?没有内置的“安置删除”来匹配安置新的原因是没有一般的方法来保证它将被正确地使用。C++类型系统中没有任何东西允许我们推断p1指向在a1中分配的对象。指向任何分配到任何地方的X的指针都可以分配给p1。

该链接的其余部分将描述如何纠正这种情况。

票数 4
EN

Stack Overflow用户

发布于 2009-11-27 18:25:30

没有诸如“安置删除”这样的术语。正如您已经说过的,如果您分配了一些新的位置,那么当需要释放时,您需要手动调用析构函数,然后还需要处理分配给新位置的实际内存缓冲区。

但是,如果不手动跟踪您自己的分配大小,您要做的事情是不可能的。原因是“放置新”的全部目的是将分配与对象初始化分离开来。因此,在布局新的情况下,分配内存缓冲区的行为完全不同于构造或销毁任何对象可能(或不可能)生活在该缓冲区中的对象。

因此,例如,如果您分配了一些缓冲区,比如char buf[1000],然后使用新位置来在该缓冲区中构造一个Foo对象数组,那么C++应该在哪里存储数组大小信息?它不会将它存储在您的缓冲区中,因为它不知道您想使用该缓冲区做什么。因此,这取决于您记录每个分配的大小,然后适当地将其与取消分配相结合。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/1809705

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档