新对象的自定义销毁和重新分配

问题描述 投票:0回答:1

给定一个从

new
表达式获得的指针,用显式调用析构函数(或
delete
)然后调用
std::destroy_at
来替换相应的
operator delete
表达式是否合法?

在我的项目中,我使用了自定义形式的 RTTI。对于具有动态调度析构函数的类型,

delete

 表达式不会调用适当的析构函数,而是调用基类的析构函数。为了解决这个问题,我“手动”(通过我的 RTTI 机器)根据分配对象的运行时类型调用正确的析构函数,然后直接调用 
operator delete
 来释放内存。

这个解决方案在我的项目中运行良好(用 clang 和 msvc 编译),但我想知道这是否会调用未定义的行为。

关于virtual

我当然可以通过使它成为virtual
来实现析构函数的动态调度。然而,这为对象提供了一个 vtable 指针,这在功能上是不需要的,因为我已经在对象中有运行时类型信息。所以 
virtual
 析构函数解决了这个问题,但是 a) 引入了不必要的运行时开销,更重要的是 b) 它解决了一个已经解决的问题,所以我认为这是一个设计缺陷。

代码示例:

使用C++提供的动态调度机制,问题将由

virtual

析构函数解决。

struct Base { virtual ~Base(); }; struct Derived: Base { // My data members which want to be properly destroyed }; // ... Base* p = new Derived(/* ... */); // ... delete p; // Derived::~Derived() is being called.
但是由于多种原因,我有一个自定义形式的 RTTI(它解决了 

virtual

 函数无法解决的问题)。

struct Base { protected: int typeID = ID_OF_TYPE_BASE; }; struct Derived1: Base { Base() { typeID = ID_OF_TYPE_DERIVED1; } }; struct Derived2: Base { Base() { typeID = ID_OF_TYPE_DERIVED2; } };
给定 

p

Base
 的指针,我可以根据 
*p
 的实际运行时类型调度函数,类似于 
std::visit
 上的 
std::variant
:

visit(p, [](auto* q) { // Here *q is statically of the runtime type of *p, // so either Base, Derived1 or Derived2. });
这解决了与 

virtual

 关键字相同的问题(除了类层次结构不易扩展且编写起来不那么漂亮),所以我可以像这样分派到正确的析构函数:

visit(p, [](auto* q) { std::destroy_at(q); });
所以我在问以下是否是合法的 C++:

Base* p = new Derived1(/* ... */); // ... // delete p; // Calls Base::~Base() - potential memory leak because subobjects of *p are not destroyed. visit(p, [](auto* q) { std::destroy_at(q); // Calls Derived1::~Derived1() ::operator delete(q); // Deallocate through pointer to derived type });

我不是在征求使用定制 RTTI 替代品是否合理的建议。

我也不想暗示使用原始

new

delete
是很好的做法,这当然隐藏在
unique_ptr
的背后。

实际代码:

https://github.com/chrysante/scatha/blob/main/lib/Common/UniquePtr.h

c++ undefined-behavior rtti
1个回答
0
投票
在 C++ 中有一个(新的)说法,可以发现每一行代码都有未定义的行为。甚至像“p++”这样的东西也可以是 UB。所以合法与否在 C++ 中是一个相当大的挑战。

现在,它有效吗?如果你没有做任何不寻常的事情,那可能是,是的。在过去的 15 年左右的时间里,我一直在这样做。我也用内存池做自定义 RTTI,它工作得很好。

只是为了教学,我写了一个例子,在内存池之上使用带有自定义删除的侵入式指针。你可以

在Github上抓取它

一般来说,我想到的唯一要求是,如果您的类是多态的,并且您要使用指向基类的指针进行删除,则基类必须是虚拟的。

© www.soinside.com 2019 - 2024. All rights reserved.