2

这是一个问题场景:我有一个 C++ 对象,其中包含一个需要在对象被销毁之前调用的 Cleanup() 虚拟方法。为了做正确的事,这个 Cleanup 方法需要访问完整的对象(包括子类数据),所以我不能只在我自己的类的析构函数开始时调用 Cleanup(),因为当我的类的当调用析构函数时,任何子类的析构函数都已经完成,并且它们可能已经释放了 Cleanup() 需要查看的一些数据。

显而易见的解决方案是要求调用代码在删除对象之前手动调用我的例程:

theObject->Cleanup();
delete theObject;

但是这个解决方案很脆弱,因为迟早有人(可能是我)会忘记调用 Cleanup() 并且会发生坏事。

另一种解决方案是拥有一个实现 pImpl 技术的“持有人”对象来包装类,并在删除对象之前让持有人对象的析构函数调用该对象的 Cleanup();但该解决方案也不是 100% 可取的,因为它使该类与标准 C++ 类的工作方式不同,并且无法在堆栈上或静态分配对象。

所以问题是,是否有一些聪明的技术(在 C++ 或 C++11 中)可以用来告诉编译器在调用第一个子类析构函数之前自动调用我的对象上的指定(可能是虚拟的)方法?

(想想看,自动调用 Init() 方法的类似机制——就在最后一个子类构造函数完成之后——也可能很方便)

4

5 回答 5

6

横向思考:摆脱Cleanup方法,这就是virtual析构函数的用途。

析构函数的真正目标virtual是允许在delete basepointer;执行时调用最外层的派生类析构函数;正如 RAII 所展示的,清理析构函数的工作。

现在你可能会争辩说你想要一个早期的Cleanup方法,以减轻析构函数的任务或者重用对象(Reset在某种程度上有点像方法)。在实践中,它很快变成了维护的噩梦(太容易忘记清理一个字段并从一种使用状态泄漏到另一种使用状态)。

所以问自己一个问题:为什么不将析构函数用于创建它的目的?

于 2012-06-01T06:22:43.680 回答
3

您可以在 PIMPL 包装器上使用一个扭曲:

class PIMPL
{
    MyDataThatNeedsInitDestroy    object;

  public:
    PIMPL(Atgs a)
       : object(a)
    {
        object.postCreationInit();
    }

    ~PIMP()
    {
        object.preDestructionDestory();
    }

    // All other methods are available via -> operator
    MyDataThatNeedsInitDestroy* operator->() { return &object);
};
于 2012-06-01T03:59:42.343 回答
1

是的,这可以通过虚拟继承来完成,因为虚拟基础子对象总是由最派生的类型构造(和销毁),并且基类的构造(和销毁)顺序也是明确定义的。

当然,您也可以将delete运算符设为私有,并提供一个由 ButRelease()组成的成员函数Cleanup(); delete this; ,这对堆栈分配的对象没有帮助。

于 2012-06-01T03:51:12.420 回答
0
struct B
{
    void cleanup()
    {
        if (!m_bCleanedUp)
        {
            m_bCleanedUp = true;
            ...
        }
    }

    virtual B::~B()
    {
        assert(m_bCleanedUp);
        ...
    }

    bool m_bCleanedUp = false;
};

struct D : B
{
    D::~D()
    {
        cleanup(); // if D author forgets, assert will fire
        ...
    }
};
于 2012-06-01T04:06:03.377 回答
-1

有没有办法让 C++ 类在它开始执行析构函数之前自动执行一个方法?

您需要将智能指针(shared_ptr - 在 boost 或 c++11 中可用)与自定义删除器一起使用,而不是原始指针。

typedef shared_ptr<MyClass> MyClassPtr;
class MyClassDeleter{
public:
    void operator()(MyClass* p) const{
        if (p)
            p->cleanup();
        delete p;
    }
};

...

MyClassPtr ptr(new MyClass, MyClassDeleter());

- 编辑 -

这将适用于动态分配的对象。对于堆栈分配的对象没有解决方案(我能想到),因此合理的做法是使构造函数私有并创建将 shared_ptr 返回给构造对象的友元工厂方法 - 假设您确实需要这种机制。

于 2012-06-01T04:09:55.590 回答