不调用对象的析构函数是未定义的行为吗?
Is it undefined behaviour to not call the destructor of an object?
这似乎是一个显而易见的问题,但我一直无法在任何地方找到明确写下的答案。考虑以下代码:
{
std::aligned_storage_t<sizeof(int), alignof(int)> storage;
int& i = *new(&storage) int;
}
在这里,我开始了一个名为 i
的对象的生命周期,但是这个生命周期没有明确的 "end",因为不应该调用 ~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
对象的析构函数调用也是如此。
不,这不是未定义的行为。不需要调用对象的析构函数。 (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:在最后一个块中,由于没有调用析构函数,因此会发生内存泄漏。但是内存泄漏是允许的。
这似乎是一个显而易见的问题,但我一直无法在任何地方找到明确写下的答案。考虑以下代码:
{
std::aligned_storage_t<sizeof(int), alignof(int)> storage;
int& i = *new(&storage) int;
}
在这里,我开始了一个名为 i
的对象的生命周期,但是这个生命周期没有明确的 "end",因为不应该调用 ~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
对象的析构函数调用也是如此。
不,这不是未定义的行为。不需要调用对象的析构函数。 (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:在最后一个块中,由于没有调用析构函数,因此会发生内存泄漏。但是内存泄漏是允许的。