C++ 什么';使用placement new[]有什么问题吗?做

C++ 什么';使用placement new[]有什么问题吗?做,c++,placement-new,C++,Placement New,考虑下面的程序。它已从一个复杂的案例简化。除非我在Obj类中删除虚拟析构函数,否则它在删除之前分配的内存时失败。我不明白为什么只有在虚拟析构函数存在的情况下,程序输出的两个地址才会不同 // GCC 4.4 #include <iostream> using namespace std; class Arena { public: void* alloc(size_t s) { char* p = new char[s]; cout <

考虑下面的程序。它已从一个复杂的案例简化。除非我在Obj类中删除虚拟析构函数,否则它在删除之前分配的内存时失败。我不明白为什么只有在虚拟析构函数存在的情况下,程序输出的两个地址才会不同

// GCC 4.4
#include <iostream>

using namespace std;

class Arena {
public:
    void* alloc(size_t s) {
        char* p = new char[s];
        cout << "Allocated memory address starts at: " << (void*)p << '\n';
        return p;
    }

    void free(void* p) {
        cout << "The memory to be deallocated starts at: " << p << '\n';
        delete [] static_cast<char*> (p); // the program fails here
    }
};

struct Obj {
    void* operator new[](size_t s, Arena& a) {
        return a.alloc(s);
    }

    virtual ~Obj() {} // if I remove this everything works as expected

    void destroy(size_t n, Arena* a) {
        for (size_t i = 0; i < n; i++)
            this[n - i - 1].~Obj();
        if (a)
            a->free(this);
    }
};


int main(int argc, char** argv) {
    Arena a;

    Obj* p = new(a) Obj[5]();
    p->destroy(5, &a);

    return 0;
}
//GCC 4.4
#包括
使用名称空间std;
班级竞技场{
公众:
空*所有(尺寸){
char*p=新字符[s];

cout
这不是第
char*p=new char[s]行的
new
返回的指针;
您可以看到大小
s
大于5个
Obj
实例在附加内存中,包含数组长度5,紧跟在
中包含的地址之前

好的,规格说明很清楚:

2.7阵列操作员新Cookies

当使用运算符new创建新数组时,通常会存储cookie以记住分配的长度(数组元素的数量),以便可以正确地解除分配

具体而言:

如果数组元素类型T有一个平凡的析构函数(12.4[class.dtor]),并且通常的(数组)释放函数(3.7.3.2[basic.stc.dynamic.deallocation])不带两个参数,则不需要cookie


因此,析构函数的虚拟性是不相关的,重要的是析构函数是非平凡的,您可以通过删除析构函数前面的关键字
virtual
轻松检查它,并观察程序崩溃。

基于chills的回答,如果您想让它“安全”:

#包括
a->free(这个-(std::has_-triple_-destructor::value?1:0));

是的,我并不真正关心大小。我关心的是这与alloc()为放置对象而给出的指针不匹配的原因。@Martin,因为
alloc()已经为数组的长度分配了额外的空间,数组元素也跟着。我现在不知道为什么它不存储数组长度,如果没有虚拟析构函数,就必须检查C++的ABI规范。这是什么规格?我的C++规范没有提到cookies。@ CharlesBailey,GCC C++ + ABI规范,@谢谢。也许你应该在你的答案中引用你所引用的内容。不是,不是真的,可能是
a->free((std::size\u t*)这个-(std::has_平凡的析构函数::value?1:0));
,但严格地说,它是UD,我相信它会在一些模糊的角落和/或不同的编译器中中断;)请注意,您确实需要一个匹配的放置数组删除,以防新表达式引发异常。
#include <type_traits>

a->free(this - (std::has_trivial_destructor<Obj>::value ? 1 : 0));