重载new/delete运算符和基类

3
我现在头疼得厉害。
基本上我有这个:
Class A -> Class B
Class A -> Class C
Class A -> Class D

Class E with constructor with declaration E(int, A *objptr, bool IsObjPtrOwner = true)

如您所见,B、C和D都继承自A,其中A是基类。由于在D内部使用了SSE2,因此D必须具有特定的对齐方式,这就是为什么我在其中重载了new/delete,每次对象动态分配时提供一个对齐的内存块。此外,由于A、B、C和D之间不同,因此我相当确定B、C、A和D的大小根本不相同。

因此,我希望能够做到这一点:

E eobj(12, new D(2.001), true);

由于参数IsObjPtrOwner将为true,因此我编写了我的类E,如果成员m_IsObjPtrOwner为真,则在E的析构函数中删除指针m_objptr。
这对我来说非常方便,因为我可以直接动态分配A的派生类之一来构造对象E,而不必担心以后的问题。原因是我将在整个程序中创建许多类E的实例,每次都有一个不同的B/C/D实例。因此,我希望不必每次构造E的实例时都保留我创建的每个指针的副本。
所以我试图使new/delete运算符成为纯虚函数,但它就是行不通。该死的函数必须是静态的,非常烦人。所以我尽可能地绕过这个问题,但最终发现我无法在静态函数中使用"this"。
我该怎么办?我如何实现这个梦想?我有一个梦想...
编辑:由于某些原因,人们完全不理解我想说什么。
我有一个基类A和一组从A派生的类B/C/D。我有一个类E,在其构造函数中以A类指针作为参数,并将其存储在一个成员变量中,例如m_bcdobj,因此我有以下内容:
class B : public A {
    B(double x) : m_x(x) { bla bla bla}
    void *operator new(size_t size)   { return Util_MemAlign(size, 4); }
    void  operator delete(void* ptr)  { Util_AlignFree(ptr); }
}
class C : public A {
    C(double x) : m_x(x) { bla bla bla}
    void *operator new(size_t size)   { return malloc(size); }
    void  operator delete(void* ptr)  { free(ptr); }
}
class D : public A {
    D(double x) : m_x(x) { bla bla bla}
    void *operator new(size_t size)   { return Util_MemAlign(size, 16); }
    void  operator delete(void* ptr)  { Util_AlignFree(ptr); }
}

如您所见,它们每个都有不同的对齐要求。
现在我有一个E类:
class E {
    public:
        E(int z, A *bcdobj, bool IsObjPtrOwner = true) : m_z(z), m_bcdobj(bcdobj), m_freebcd(IsObjPtrOwner) { bla bla bla }

        ~E() { if (m_freebcd) { delete m_bcdobj; } }

    private:
        A *m_bcdobj;
        int m_z;
        bool m_freebcd;
}

所以我想要能够做到那件事情:
E einst(2, new D(2.001));

即我不保留分配的D对象的副本。分配的D对象将在“einst”被销毁时释放。问题是这段代码将无法正常工作。在~E()中删除m_bcdobj时,D内部的重载删除操作符将不会被调用。
谢谢!

3
我不懂问题出在哪里。你在调用构造函数时不想动态分配 A* objptr 吗?你不想跟踪它吗?这一切与重载的 new/delete 运算符有什么关系? - Chad
正如所说,B/C/D必须使用特定的对齐方式进行分配,除非我在它们每个都重载new/delete,否则这是不可能发生的。此外,是的,我想在构造E时直接动态分配B/C/D,然后不再关心它。基本上,E的实例将在E自身被销毁时负责删除它。 - Yannick
“operator delete”是静态函数,但仍然会进行动态分派,如果这是你的问题的话。 - T.C.
我是否应该添加。我面临的问题是,当我在E的析构函数中删除A *objptr时,“delete objptr;”这一行不会调用类B/C/D的重载“delete”运算符,因为它会退回到与基类A相关联的运算符。 - Yannick
2
@Yannick,A有虚析构函数吗? - T.C.
刚刚编辑了我的帖子,伙计们! - Yannick
1个回答

7

operator delete是特殊的,尽管它是一个静态成员,但如果类有虚析构函数,则会动态调度。 §12.5 [class.free]/p4:

如果delete-expression用于释放具有虚析构函数的类对象,那么在动态类型的虚拟析构函数(12.4)的定义点选择相应的释放函数。

例如,

struct B {
    virtual ~B() = default;
    void operator delete(void* ptr) { 
        std::cout << "B's operator delete" << std::endl; ::operator delete(ptr); 
    }
};
struct D : B {
    void operator delete(void* ptr) { 
        std::cout << "D's operator delete" << std::endl; ::operator delete(ptr); 
    }
};
int main() {
    B* bp = new D;
    delete bp; //1: uses D::operator delete(void*)
}

prints:

D's operator delete

因此,给A一个虚析构函数,你应该能看到正确的operator delete被调用 :)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接