是否允许显式调用析构函数,然后在具有固定生存期的变量上放置新的?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (58)

我知道,显式调用析构函数会导致未定义的行为,这是因为双重析构函数调用,如下所示:

#include <vector>

int main() {
  std::vector<int> foo(10);
  foo.~vector<int>();
  return 0;  // Oops, destructor will be called again on return, double-free.
}

但是,如果我们把新的位置称为“复活”对象呢?

#include <vector>

int main() {
  std::vector<int> foo(10);
  foo.~vector<int>();
  new (&foo) std::vector<int>(5);
  return 0;
}

更正式地:

  1. 如果我显式地调用某个对象的析构函数,而这个对象不是用新的位置构造的(例如,它要么是局部变量/全局变量,要么是用以下变量分配的),那么在C++中会发生什么,new)然后,在这个对象被破坏之前,调用新的位置来“恢复”它?
  2. 如果可以的话,是否保证所有对该对象的非Const引用也都是安全的,只要我在对象“死”时不使用它们?
  3. 如果是这样的话,是否可以使用一个非Const引用来重新放置新的对象?
  4. 那康斯特的推荐信呢?

示例usecase:我想“重新分配”一个没有operator=.

这个问题是“覆盖”具有非静态的对象。const会员是非法的。把这个问题的范围限制在没有任何const各位议员。

提问于
用户回答回答于

首先,[basic.life]/8清楚地指出,任何指针或对原始foo将引用您在foo in your case. In addition, the name foo将引用在那里构造的新对象([basic.life]/8)

第二,你确保有一个原始类型的对象用于存储。foo在退出其作用域之前,如果有任何抛出,则必须捕获它并终止程序([basic.life]/9)

  • (8)如果在某一对象的生存期结束后,在该对象占用的存储空间被重用或释放之前,将在该原始对象所占用的存储位置创建一个新对象,指向该原始对象的指针,一个引用该原始对象的引用,或该原始对象的名称将自动引用该新对象,并且一旦生命周期结束,该对象的名称将自动引用该新对象。的新对象,可以用来操作新对象,如果:
- (8.1) the storage for the new object exactly overlays the storage location which the original object occupied, and
- (8.2) the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and
- (8.3) the type of the original object is not const-qualified, and, if a class type, does not contain any non-static   data member whose type is const-qualified or a reference type, and
- (8.4) the original object was a most derived object (1.8) of type   T and the new object is a most derived   object of type T (that is, they are not base class subobjects).
  • (9)如果程序以静态(3.7.1)、线程(3.7.2)或自动(3.7.3)存储持续时间结束T类型对象的生存期,而如果T具有一个非平凡的析构函数,则程序必须确保在发生隐式析构函数调用时,原始类型的对象占据相同的存储位置;否则程序的行为未定义。即使在异常情况下退出块,也是如此。

手动运行析构函数并进行新的放置。像这样简单的事情operator=,除非您正在编写自己的变体/任何/向量或类似类型。

用户回答回答于

下面是一个演示此行为的示例程序:

#include <iostream>
#include <stdexcept>
using namespace std;

struct Foo
{
    Foo(bool should_throw) {
        if(should_throw)
            throw std::logic_error("Constructor failed");
        cout << "Constructed at " << this << endl;
    }
    ~Foo() {
        cout << "Destroyed at " << this << endl;
    }
};

void double_free_anyway()
{
    Foo f(false);
    f.~Foo();

    // This constructor will throw, so the object is not considered constructed.
    new (&f) Foo(true);

    // The compiler re-destroys the old value at the end of the scope.
}

int main() {
    try {
        double_free_anyway();
    } catch(std::logic_error& e) {
        cout << "Error: " << e.what();
    }
}

这些指纹:

建于0x7fff41ebf03f 0x7fff41ebf03f销毁 0x7fff41ebf03f销毁 错误:构造函数失败

扫码关注云+社区