C++:使用仅具有属性的继承结构时是否需要实现虚拟析构函数?
C++: Implementation of virtual destructor necessary when using inherited structs with only properties?
我知道我需要定义一个虚拟析构函数(最好的选择,即使我的 class 是 final
)。
就我而言,我正在使用类似 C 的结构(没有函数,没有默认值,只有普通成员)并使用继承来组成一个新结构。然后我在 std::unique_ptr
中存储一个指向基 class 的指针,让 RAII 完成剩下的工作。
我现在很好奇是否还需要显式添加虚拟析构函数以避免内存问题。
例如:
#include <chrono>
#include <memory>
struct A {
std::chrono::milliseconds duration = std::chrono::milliseconds{-1};
int count = 0;
};
struct B {
int mode = 0;
};
struct C : public A, public B {
int foo = 1;
};
int main()
{
std::unique_ptr<A> base = std::make_unique<C>();
base.reset(); // I expect here that A,B and C are destructed properly
return 0;
}
class 是多态的还是平凡的都没有关系。
如果 delete
是在与它指向的对象的 most-derived 类型不同类型(最多 cv-qualification)的指针上调用的,而 pointed-to-type 则不没有虚拟析构函数,则行为未定义。
此规则的一个明显原因是基础 class 子对象可能与 most-derived 对象不在同一地址。因此编译器无法知道传递给释放函数的偏移量需要是多少。
有人可能会争辩说,如果使用指向第一个基 class 子对象的指针,具有普通析构函数的 standard-layout class 不需要遵循此规则,但是标准没有例外,你的 class C
也不是 standard-layout。
参见 CWG issue 1259 关闭为 not-a-defect。问题中提到的 size-aware 全局释放函数也在 C++14 中引入,这是使用基 class 指针可能会给您带来实际问题的另一个原因,即使析构函数是微不足道的并且没有偏移量的地址。
如果您使用 shared_ptr
而不是 unique_ptr
那么这将起作用,因为 shared_ptr
的删除器是在创建第一个指针时创建的,并且在创建指针时不会更改在 shared_ptr
个实例之间复制。这不适用于将使用 std::default_delete<T>
的 unique_ptr
,其中 T
是 unique_ptr
实例的类型,而不是最初构造的类型。
有关 unique_ptr
失败和 shared_ptr
工作的一些示例,请参阅 https://godbolt.org/z/TjP6dbo9G。
我知道我需要定义一个虚拟析构函数(最好的选择,即使我的 class 是 final
)。
就我而言,我正在使用类似 C 的结构(没有函数,没有默认值,只有普通成员)并使用继承来组成一个新结构。然后我在 std::unique_ptr
中存储一个指向基 class 的指针,让 RAII 完成剩下的工作。
我现在很好奇是否还需要显式添加虚拟析构函数以避免内存问题。
例如:
#include <chrono>
#include <memory>
struct A {
std::chrono::milliseconds duration = std::chrono::milliseconds{-1};
int count = 0;
};
struct B {
int mode = 0;
};
struct C : public A, public B {
int foo = 1;
};
int main()
{
std::unique_ptr<A> base = std::make_unique<C>();
base.reset(); // I expect here that A,B and C are destructed properly
return 0;
}
class 是多态的还是平凡的都没有关系。
如果 delete
是在与它指向的对象的 most-derived 类型不同类型(最多 cv-qualification)的指针上调用的,而 pointed-to-type 则不没有虚拟析构函数,则行为未定义。
此规则的一个明显原因是基础 class 子对象可能与 most-derived 对象不在同一地址。因此编译器无法知道传递给释放函数的偏移量需要是多少。
有人可能会争辩说,如果使用指向第一个基 class 子对象的指针,具有普通析构函数的 standard-layout class 不需要遵循此规则,但是标准没有例外,你的 class C
也不是 standard-layout。
参见 CWG issue 1259 关闭为 not-a-defect。问题中提到的 size-aware 全局释放函数也在 C++14 中引入,这是使用基 class 指针可能会给您带来实际问题的另一个原因,即使析构函数是微不足道的并且没有偏移量的地址。
如果您使用 shared_ptr
而不是 unique_ptr
那么这将起作用,因为 shared_ptr
的删除器是在创建第一个指针时创建的,并且在创建指针时不会更改在 shared_ptr
个实例之间复制。这不适用于将使用 std::default_delete<T>
的 unique_ptr
,其中 T
是 unique_ptr
实例的类型,而不是最初构造的类型。
有关 unique_ptr
失败和 shared_ptr
工作的一些示例,请参阅 https://godbolt.org/z/TjP6dbo9G。