【问题标题】:Invoke destructor manually and reuse memory手动调用析构函数并重用内存
【发布时间】:2013-06-17 18:45:13
【问题描述】:

虽然我知道这可能不是最好的想法,但我假设性地问:

调用对象的(即定义的行为)是否合法? 手动析构,然后将内存重用于另一个对象?

定义:

class A {
  int a, b, c;
  A() {}
  ~A() {}
}

A createObject() {
  A object;
  return object;
}

代码:

A* object = new A();
// Use object...
object->~A();
*object = createObject();

【问题讨论】:

    标签: c++ destructor explicit-destructor-call


    【解决方案1】:

    你不能像你正在做的那样在一个被破坏的对象上调用赋值运算符。至少一般不会,尽管它可能适用于某些类型。不过,您可以这样做:

    A* object = new A();
    object->~A();
    // Placement new.
    // Constructs a new object in the memory pointed to by object pointer.
    // Doesn't allocate memory.
    new (object) A(createObject());
    

    您甚至可以使用placement new 在该内存中构造不同类型的对象,只要它们适合。只需确保两件事:

    1. 永远不要在尚未销毁的现有对象之上构造对象。
    2. 永远不要在指针上调用delete 之后,在指针指向的对象上调用析构函数,直到在其位置重新构造一个(相同类型的)对象。

    【讨论】:

      【解决方案2】:

      显式调用析构函数是合法的 - 事实上,当您使用 placement new 时,您就是这样做的。当内存已分配时,“就地”初始化对象也是合法的做法,但您应该采取不同的做法:您可以使用以下语法,而不是使用赋值运算符:

      object = new (object) A(); // Placement syntax
      

      你这样做的方式(使用赋值运算符)是不正确的,因为你在一个析构函数已经完成的对象上调用一个函数(即赋值运算符)。

      【讨论】:

      • 在内存已分配时“就地”初始化对象也是合法的做法,前提是为旧对象分配的内存足以容纳新对象。对于某些分配器,我认为如果对象的大小不同(分配器可能希望释放的内存少于分配的内存),我认为应该小心...
      • @Massa 如果对象在内部管理内存,这可能不起作用。例如,考虑一个对象,它有一个指向 C 字符串的指针,它在析构函数中将其删除。根据三规则,对象还必须删除或以其他方式管理赋值运算符中的同一指针。但是,在调用析构函数后指针会失效,这可能会导致赋值运算符出现未定义的行为。
      • 我认为你误解了我的回答:我只是在限定你的短语>...例如,如果新对象比旧对象大,或者它们具有不同的分配器,或者它们具有不同的大小并且分配器在解除分配时考虑了对象的大小,这可能是不合法的。
      • @Massa - 但是,在原始问题中,如果您使用显式析构函数调用和放置 new 来放入与原始对象不同的类型,那么您将获得未定义的行为;当对象超出范围时,编译器将调用原始类型的析构函数。
      • delete 会这样做吗?因为在原来的问题中,析构函数只在delete上被调用,它是一个指针!
      猜你喜欢
      • 2021-04-24
      • 1970-01-01
      • 2014-01-15
      • 2017-08-28
      • 2020-05-25
      • 2019-11-24
      • 2016-01-29
      • 2013-06-01
      • 2018-12-19
      相关资源
      最近更新 更多