【问题标题】:Is it undefined behaviour to not call the destructor of an object?不调用对象的析构函数是未定义的行为吗?
【发布时间】:2020-09-25 16:58:15
【问题描述】:

这似乎是一个显而易见的问题,但我无法在任何地方明确找到答案。考虑以下代码:

{
    std::aligned_storage_t<sizeof(int), alignof(int)> storage;
    int& i = *new(&storage) int;
}

在这里,我开始了一个名为i 的对象的生命周期,但是这个生命周期没有明确的“结束”,因为不应该调用~int。 我不确定这个问题的答案是否取决于类型是否微不足道,所以我将提供第二个示例:


class MyMem : public std::pmr::memory_resource
{
    std::size_t mem_size = 0u;
    char* mem_handle = nullptr;

    virtual void* do_allocate(std::size_t bytes, std::size_t) final
    {
        mem_size += bytes;
        mem_handle = static_cast<char*>(std::realloc(mem_handle, mem_size));
        return mem_handle + mem_size - bytes;
    }
    virtual void do_deallocate(void*, std::size_t, std::size_t) final {}
    virtual bool do_is_equal(const std::pmr::memory_resource&) const noexcept { return false; }

public:
    void* data() const noexcept { return mem_handle; }
};

//...
MyMem mbr;
{
    std::aligned_storage_t<sizeof(std::pmr::vector<int>), alignof(std::pmr::vector<int>)> vec_storage;
    auto& vec = *new(&vec_storage) std::pmr::vector<int>(&mbr);
    vec.resize(N);
}
// Free the data here
std::free(mbr.data());

使用自定义内存资源,我可以获得对vector::resize 调用分配的数据的句柄,并确保我们不会泄漏内存。然而,vector 的实际析构函数调用丢失了,该内存中分配的每个 int 对象的析构函数调用也丢失了。

【问题讨论】:

  • @AsteroidsWithWings 感谢您的链接,非常有趣。我将其解读为,no 没有未定义的行为,因为我不“依赖析构函数产生的副作用”?
  • 向量 dtor 可能由于释放而产生副作用。不过我不知道pmr。这基本上意味着只要 dtor 微不足道,您就是安全的。然而,措辞非常模糊,我尽量避免再次猜测它。为什么不直接破坏
  • 我想我现在需要定义“任何依赖于析构函数产生的副作用的程序”,我的例子是否依赖于~vector 的副作用?我不会这么说,那样的话就不会有UB。
  • 能够“释放”vector 使用的内存对于转移所有权非常有用。

标签: c++ c++17 language-lawyer destructor


【解决方案1】:

不,这不是未定义的行为。不需要调用对象的析构函数。 (C++ standard example)

N.B:然而,在您的示例中,由放置 new 表达式创建的 int 对象的生命周期在右大括号处结束。如果对象的析构函数被调用,或者它的存储被释放或重用[basic.life]/1,则对象生命周期结束@:

{
  std::aligned_storage_t<sizeof(int), alignof(int)> storage;
  int& i = *new(&storage) int;
} // storage released => end of life of the int.

{
  std::aligned_storage_t<sizeof(std::pmr::vector<int>),
                                alignof(std::pmr::vector<int>)> vec_storage;
  auto& vec = *new(&vec_storage) std::pmr::vector<int>(&mbr);
  vec.resize(N);
} // end of life of the vector, without destructor call

{
  std::aligned_storage_t<sizeof(std::pmr::vector<int>),
                                alignof(std::pmr::vector<int>)> vec_storage;
  auto& vec = *new(&vec_storage) std::pmr::vector<int>(&mbr);
  vec.resize(N);
  // next statement end the life of the vector refered by vec, no destructor called
  auto& vec2 = *new(&vec_storage) std::pmr::vector<int>(&mbr);
} // end of life of the vector referred by vec2, no destructor called

N.B.2:在最后一个块中,由于没有调用析构函数,您将有内存泄漏。但内存泄漏是允许的。

【讨论】:

  • 感谢您的回答,如果您在第一句话中添加引用,我将很乐意接受。
  • @nitronoid 我找不到报价。但我在 C++ 标准中找到了一个例子。我希望它会足够令人满意。
猜你喜欢
  • 2011-09-07
  • 1970-01-01
  • 1970-01-01
  • 2014-08-11
  • 1970-01-01
  • 2011-03-18
  • 2017-03-27
  • 2011-06-19
  • 2015-11-19
相关资源
最近更新 更多