bpm*_*non 9 c++ memory multiple-inheritance
我有一个程序,它使用自定义分配器和解除分配器来管理内存。我最近遇到了一个泄漏,它使我陷入了一个巨大的兔子洞,最终导致自定义删除器无法处理多重继承。在下面的代码示例中:
#include <iostream>
#include <memory>
using namespace std;
class Arena {};
void* operator new(std::size_t size, const Arena&) {
auto ptr = malloc(size);
cout << "new " << ptr << endl;
return ptr;
}
void operator delete(void* ptr, const Arena&) {
cout << "delete " << ptr << endl;
free(ptr);
}
class A
{
public:
virtual ~A() = default;
};
class B
{
public:
virtual ~B() = default;
};
class AB : public A, public B
{
public:
~AB() override = default;
};
int main()
{
B* ptr = new (Arena()) AB;
ptr->~B();
operator delete(ptr, Arena());
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出是:
new 0x55e20c8a6eb0
delete 0x55e20c8a6eb8
free(): invalid pointer
Run Code Online (Sandbox Code Playgroud)
因为B的地址是AB内部某处的vtable。使用内置delete ptr函数会导致指针返回到其原始值并成功释放。我在这里找到了一些有关 top_offset 的信息用于解决此问题,但这取决于实现。那么,有没有一种方法可以在不了解 AB 的情况下将指向 B 的指针转换回指向 AB 的指针呢?
你可以这样做:
void* dptr = dynamic_cast<void*>(ptr);
ptr->~B();
operator delete(dptr, Arena());
Run Code Online (Sandbox Code Playgroud)
请注意,您需要在销毁对象之前B进行dynamic_cast 。
如果没有 RTTI,事情就会变得棘手。我假设在您的真实代码中,您需要 arena 对象的标识(否则定义成员运算符 new/delete 会很简单,它只是凭空拉出一个 arena 并重定向到全局放置 new/delete )。您需要将此身份存储在某处。嗯,如果我们只能为其动态分配一些内存就好了……等一下……我们正在分配内存,我们可以将它存储在那里,只需适当增加大小即可……
union AlignedArenaPtr {
Arena* arena;
std::max_align_t align;
};
struct Base { // inherit everything from this
virtual ~Base() = default;
void* operator new(std::size_t size, Arena *arena) {
auto realPtr = (AlignedArenaPtr*)::operator new(size +
sizeof(AlignedArenaPtr), arena);
realPtr->arena = arena;
return realPtr + 1;
}
void operator delete(void* ptr) {
auto realPtr = ((AlignedArenaPtr*)(ptr)) - 1;
::operator delete(realPtr, realPtr->arena);
}
void* operator new(std::size_t size) = delete; // just in case
};
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
331 次 |
| 最近记录: |