是否适合在派生类析构函数中调用虚函数?

时间:2014-04-23 05:57:03

标签: c++

我有一个继承,即基类的析构函数适用Template Method Pattern。析构函数必须在调用虚拟清理函数之前完成一些工作,并在调用之后再执行一些工作。

我们知道Never Call Virtual Functions during Construction or Destruction。因此,以下代码肯定不可用。

class base
{
public:
    virtual ~base()
    {
        // ... do something before do_clear()
        do_clear();
        // ... do something after do_clear()
    }

private:
    virtual void do_clear() = 0;
};

class d1
    : public base
{
public:
    d1() : m_x(new int) {}
    ~d1() {}

private:
    virtual void do_clear()
    {
        delete m_x;
    }
    int *m_x;
};

但是如果我将进程的破坏移动到派生类的析构函数中,例如:

class base
{
public:
    virtual ~base()
    {
    }

protected:
    void clear()
    {
        // ... do something before do_clear()
        do_clear();
        // ... do something after do_clear()
    }

private:
    virtual void do_clear() = 0;
};

class d1
    : public base
{
public:
    d1() : m_x(new int) {}
    ~d1()
    {
        clear();
    }

private:
    virtual void do_clear()
    {
        delete m_x;
    }
    int *m_x;
};

如果客户写下:

base *x = new d1;
delete x;

它会调用~d1(),然后调用base::clear(),最终正确调用虚拟函数d1::do_clear()

base::clear()可以移动到公共场所,客户端可以通过在销毁之前调用base::clear()来安全地创建内容。一个先决条件是客户必须知道并且不要忘记打电话,我认为它不方便并打破封装。

我的问题是:

  1. 设计是危险/风险吗?
  2. 现有其他更好的设计吗?

2 个答案:

答案 0 :(得分:3)

您当前的设计存在两个问题。首先是它违反了规则五/零规则。这意味着这些类的普通使用几乎肯定会导致内存泄漏或双重删除。

第二个问题是你使用继承来建模可能更好地用组合建模的东西。 base希望d1为其析构函数提供一些额外的功能,并在运行时指定此功能的确切形式。因此,可更换接口的使用是base的内部,因此不应在外部可见。

以下是我将如何编写此代码(使用wheels::value_ptr):

struct D_interface {
    //Providing virtual functions for run-time modifiable behaviour
    virtual D_interface *clone() const = 0;
    virtual ~D_interface(){}
};

struct D_Delete {
    //Here is the code to call through to the virtual `D` object behaviour:
    void operator()(D_interface *p) {
        // ... do something before delete p;
        delete p;
        // ... do something after delete p;
    }
    //Put (pointers to) relevant data here,
    //initialise them when constructing the `value_ptr`, or
    //at a later stage with get_deleter
};

struct d1 : D_interface {
    wheels::value_ptr<int> mx;
    virtual D_interface *clone() const {
        return new d1(*this);
    }
};

//Nothing derives from `base`, because the polymorphism that is needed is internal
//to the implementation of `base`
//To add new functionality, add a new `D_interface` implementation.
class base
{
    wheels::value_ptr<D_interface, wheels::DefaultCloner<D_interface>, D_Delete> D_impl;
    public:
    base(D_interface *D_impl)
        : D_impl(D_impl)
    {
    }
};

答案 1 :(得分:0)

就我而言,我认为这种模式是肯定的,因为你需要在派生类中实现虚函数。这是虚拟课程的哲学。

相关问题