我在设计使用自己的内存分配的正确类时遇到了一些麻烦。考虑一下:
class IAbstract { ... };
class CConcrete : public IAbstract { ... };
我想做这样的事情:
IAbstract *ptr = new CConcrete();
delete ptr;
问题是,我希望CConcrete的“新”使用内存分配器。另外,我希望“删除”使用propriate deallocator。但是,new和delete是静态函数,所以上面的例子中的delete不会调用CConcrete的删除(如果删除是虚拟的话应该这样做。)
解决这个问题的一种方法是制作这样的东西:
class IAbstract {
public:
virtual Delete(void* ptr)=0;
void operator delete(void* ptr) {
((IAbstract*)(ptr))->Delete(ptr);
}
};
并覆盖派生类中的Delete。但是这个解决方案非常难看,特别是将ptr转换为IAbstract *。
有没有更好的方法呢?
答案 0 :(得分:0)
答案 1 :(得分:0)
准确地知道你的代码有什么问题是很困难的,因为你已经省去了这么多。我能做的最好的就是向你展示一个有效的计划。
考虑这个程序:
#include <iostream>
#define X() (std::cout << __PRETTY_FUNCTION__ << "\n")
class IAbstract {
public:
virtual ~IAbstract() { X(); }
};
class CConcrete : public IAbstract {
public:
void* operator new(size_t sz) {
X();
return ::operator new(sz); // or however you allocate memory
}
void operator delete(void* p) {
X();
::operator delete(p); // or however you de-allocate memory
}
~CConcrete() { X(); }
};
int main () {
IAbstract *ptr = new CConcrete();
delete ptr;
}
我的电脑上的输出是:
static void* CConcrete::operator new(size_t)
virtual CConcrete::~CConcrete()
virtual IAbstract::~IAbstract()
static void CConcrete::operator delete(void*)
请注意,执行delete ptr
时,delete
正确调用虚拟析构函数和正确operator delete()
。
注意:由于使用__PRETTY_FUNCTION__
答案 2 :(得分:0)
第一个有趣的事情是你无法将你的void指针强制转换为调用任何成员函数。从技术上讲,它是编译的,但是在对象(或对象层次结构)被销毁之后(通过调用析构函数),运算符删除被称为。换句话说 - 你正在做的是UB,因为你试图访问被破坏的类的vtable。
另一件事是,只要析构函数是虚拟的,就会在类上调用适当的delete运算符。因此,没有必要使用额外的vtable资源重新创建自己的机制。我认为您正在寻找以下方法:
#include <new>
#include <cstdio>
#include <cstdlib>
class Base {
public:
Base() { }
virtual ~Base() { printf("Base::~Base()\n"); }
};
class Derived : public Base {
char data[256];
public:
Derived() {}
virtual ~Derived() { printf("Derived::~Derived()\n"); }
void *operator new(size_t size)
{
void *p = malloc(size);
printf("Allocated %lu bytes @ %p\n", size, p);
return p;
}
void operator delete(void *ptr)
{
printf("Freeing %p\n", ptr);
free(ptr);
}
};
int main()
{
Base *b = new Base();
delete b;
b = new Derived();
delete b;
}
但是不要忘记,只要删除虚拟析构函数,就不会调用重载的运算符delete。