【问题标题】:What's wrong with this use of placement new[]? do使用placement new[]有什么问题?做
【发布时间】:2011-11-24 20:44:53
【问题描述】:

考虑下面的程序。它已经从一个复杂的案例中简化了。除非我删除 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;
}

当虚拟析构函数存在时,这是我的实现中程序的输出:

分配的内存地址开始于:0x8895008 要释放的内存开始于:0x889500c

运行失败(退出值 1)

请不要问它应该做什么程序。正如我所说,它来自一个更复杂的案例,Arena 是各种类型内存的接口。在此示例中,内存只是从堆中分配和释放。

【问题讨论】:

  • 请注意,您确实需要一个匹配的放置数组删除,以防新表达式引发异常。

标签: c++ placement-new


【解决方案1】:

this不是new在char* p = new char[s];行返回的指针,可以看到s的大小大于5个Obj实例。区别(应该是sizeof (std::size_t))在额外的内存中,包含数组的长度,5,紧挨在this中包含的地址之前。

好的,规范说清楚了:

http://sourcery.mentor.com/public/cxx-abi/abi.html#array-cookies

2.7 数组运算符 new Cookies

在使用new操作符创建新数​​组时,通常会存储一个cookie来记住分配的长度(数组元素个数),以便正确解除分配。

具体来说:

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

所以,析构函数的虚特性无关紧要,重要的是析构函数是不平凡的,您可以通过删除前面的关键字virtual 轻松检查析构函数并观察程序崩溃。

【讨论】:

  • 是的,我并不关心大小。我关心这与分配对象的指针 alloc() 不匹配的原因。
  • @Martin,因为alloc() 为数组的长度分配了额外的空间,并且数组元素紧随其后。我现在真的不知道为什么如果没有虚拟析构函数它不存储数组长度,必须检查 C++ ABI 规范。
  • 这是什么规格?我的 C++ 规范没有提到 cookie。
  • @CharlesBailey,GCC C++ ABI 规范,sourcery.mentor.com/public/cxx-abi/abi.html
  • @chill:谢谢。也许您应该在答案中引用您所引用的内容。
【解决方案2】:

根据chills的回答,如果你想让它“安全”:

#include <type_traits>

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

【讨论】:

  • 不是,不是真的,也许是 a-&gt;free ((std::size_t *)this - (std::has_trivial_destructor&lt;Obj&gt;::value ? 1 : 0));,但严格来说是 UD,我相信它会在一些晦涩的极端情况和/或不同的编译器中中断;)
猜你喜欢
  • 2021-08-31
  • 1970-01-01
  • 2010-09-26
  • 2021-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-22
相关资源
最近更新 更多