我需要手动分配代码中的对象,我的分配器使用void指针和偏移量,并在必要的地方强制转换它们。这应该不是一个问题(而且它在大多数情况下都能工作),但是当我调用一个虚函数时,我得到了一个分段错误。我假设这是因为指针没有指向vtable的指针,所以它从未初始化的内存中读取函数指针并试图调用它,这会导致segfault。但是,我希望能够使用虚拟函数。我怎样才能规避这个问题呢?
MVE(我的PC(x86_64)上的Segfaults在G++10.2.0时编译,没有标志):
#include <iostream>
#include <cstdlib>
struct BaseClass {
virtual void do_stuff() = 0;
};
struct DerivedClass : public BaseClass {
virtual void do_stuff() { std::cout << "i did it!" << std::endl; }
};
int main(int argc, char const* argv[]) {
void* data = std::malloc(sizeof(DerivedClass));
DerivedClass other;
*((DerivedClass*)data) = other;
// new (data) OtherClass; // this works fine, but why?
((BaseClass*)data)->do_stuff();
return 0;
}
void* data = std::malloc(sizeof(DerivedClass));
null
((BaseClass*)data)->do_stuff();
正在将此
您需要实际创建您的派生类,例如:
auto data = new DerivedClass();
如果它必须来自某个错误的空间,你可以使用新的位置,例如:
auto data = new(buf) DerivedClass();
如果希望复制特定对象,编写复制构造函数会安全得多。
您可以使用placement new来初始化内存。
int main(int argc, char const* argv[])
{
void* data = std::malloc(sizeof(DerivedClass));
// This is placement new - it doesn't actually allocate memory
DerivedClass *dc = new (data) DerivedClass();
dc->do_stuff();
// You need to call the destructor manually if you allocated the memory with placement new.
// You can't safely use operator delete here
dc->~DerivedClass();
std::free(dc);
return 0;
}
您还可以在您想要拥有自定义内存管理的类上覆盖操作符new和delete,然后像平常一样使用它们,这可能更容易,因为现在您不必担心手动删除它们。您可能还希望以类似的方式重写运算符new[]和运算符delete[]。
struct DerivedClass : public BaseClass
{
virtual void do_stuff() { std::cout << "i did it!" << std::endl; }
static void * operator new(size_t size)
{
return std::malloc(size);
}
static void operator delete(void * p)
{
std::free(p);
}
};
int main(int argc, char const* argv[])
{
DerivedClass *dc = new DerivedClass();
dc->do_stuff();
delete dc;
return 0;
}
https://en.cppreference.com/W/cpp/memory/new/operator_new