C++ “全球”;“安置”;删除[]

C++ “全球”;“安置”;删除[],c++,visual-c++,C++,Visual C++,我正在尝试用自己的分配器替换new/delete。所以,覆盖放置新的和删除-非常高兴。看起来像这样 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();

我正在尝试用自己的分配器替换new/delete。所以,覆盖放置新的和删除-非常高兴。看起来像这样

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 );
    }
}
这里,如果存在~MyClass(),则pA的值为buf+4,分配的内存量为sizeof(MyClass)*5+4。但是,如果没有dtor,则pA==buf,分配的内存量为sizeof(MyClass)*5


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

您可以在分配器中查找指针,从簿记中找出大小,并使用sizeof T计算元素数。

如果有疑问,请咨询专家:

但是我们以后如何才能正确地删除这些对象呢?之所以没有内置的“placement delete”来匹配新的placement,是因为没有通用的方法来确保正确使用它。C++类型系统中的任何东西都不允许我们推断P1点到ALANE A1中分配的对象。可以将指向分配到任意位置的任何X的指针分配给p1


该链接的其余部分将继续介绍如何纠正这种情况。

没有“位置删除”这样的术语。正如您所说,如果您使用placement new分配了一些内容,那么在解除分配时,您需要手动调用析构函数,然后还要处理为placement new分配的实际内存缓冲区

但是,如果不手动跟踪您自己的分配大小,您尝试执行的操作是不可能的。原因是“placement new”的整个要点是将分配与对象初始化解耦。因此,在placement new中,分配内存缓冲区的行为与构建或销毁任何对象可能(或可能不)生活在该缓冲区中的行为是完全独立的


这样,例如,如果分配了一些缓冲区,比如<代码> char Buf(1000)< /C>,然后使用Puffy new构建缓冲区中的 Foo> /Cord>对象数组,C++应该存储数组大小信息?它不会将它存储在你的缓冲区中,因为它不知道你想用这个缓冲区做什么。因此,您需要记录每个分配的大小,然后将其与解除分配适当地结合起来。

简短回答:

这种用法没有直接的支持。如果使用不同的签名重载new,编译器会将其视为new重载(而不是placement new),并添加自己的簿记代码。我无法(找到)对编译器说“展开您的簿记,并调用与此签名匹配的my delete重载”-它只会在调用
void operator delete(void*p)
void operator delete[](void*p)
时插入代码以展开簿记

如果您确实使用新签名覆盖new,编译器希望您在new期间出现异常时使用匹配的签名定义delete-这是唯一一次使用它

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

长答案:

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

  • void*操作符new[](size\t sz,Allocator*a)
    重载到底是什么
  • 是否存在或不存在“位置删除”
  • 如何调用
    void操作符delete[](void*p,Allocator*a)
    ,以使编译器插入其簿记终结
  • 要点1:关于重载的讨论很多。考虑到编译器正在插入簿记代码,必须认为
    void*运算符new[](size_t sz,Allocator*a)
    声明了(非放置)new的重载。它永远不会为新的位置插入簿记代码,因为新位置的关键是你自己处理它

    第2点:R.E.“没有像placement delete这样的东西”,你会在例如VS2k8新标题中发现一些看起来非常像它的东西(并被如此评论)。它只是一个存根,用于在放置新文件时发生异常的情况。然而,看起来确实不能以有意义的方式调用placement delete

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

    就实际解决问题而言,这似乎是一场失败

    例如:

    //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. 
    
        }
    
    摘要:在启用编译器“魔法”的情况下,是否有语法允许使用扩展参数列表调用delete重载。或者,是否有方法通过替代向新放置添加参数

    可疑答案:否

    推论:您不能完全自由地偏离6个内置的新签名。这样做会导致new超负荷,编译器生成了簿记,但无法访问相应的delete以解除簿记

    警告:您可以偏离内置签名,但只需注入代码,删除时无需再次处理(例如插入)。如果您无法使用分配的
    void*运算符new(size\t s)
    版本,则delete仍将正常工作

    (一些事实陈述来自调试器中的实验,可能只适用于MSVC8(cl9)。OP坐在我旁边的桌子上。)

    您不覆盖“位置删除”
        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); }