替换基础析构函数上的虚函数调用

问题描述 投票:2回答:2

所以,我写了下面的代码。

#include <iostream>

class AbstractMachine
{
public:
    void powerOn();
    void powerOff();
    bool isPoweredOn();

    virtual ~AbstractMachine();

protected:
    virtual void powerOnImpl() = 0;
    virtual void powerOffImpl() = 0;

private:
    bool m_isPoweredOn;
};

bool AbstractMachine::isPoweredOn()
{
    return m_isPoweredOn;
}

void AbstractMachine::powerOn()
{
    if (!m_isPoweredOn)
    {
        powerOnImpl();
        m_isPoweredOn = true;
    }
}

void AbstractMachine::powerOff()
{
    if (m_isPoweredOn)
    {
        powerOffImpl();
        m_isPoweredOn = false;
    }
}

AbstractMachine::~AbstractMachine()
{
    powerOff();       // (1)
    std::cout << "Destroying a machine" << std::endl;
}

class AirConditioner : public AbstractMachine
{
protected:
    void powerOnImpl() override;
    void powerOffImpl() override;
public:
    ~AirConditioner();
};

void AirConditioner::powerOnImpl()
{
    std::cout << "Turning on air conditioner" << std::endl;
}

void AirConditioner::powerOffImpl()
{
    std::cout << "Turning off air conditioner" << std::endl;
}

AirConditioner::~AirConditioner()
{
    //powerOff();        // (2)
    std::cout << "Destroing air conditioner" << std::endl;
}


int main()
{
    AbstractMachine *machine = new AirConditioner();
    machine->powerOn();
    delete machine;
}

这将失败,因为当基础析构函数调用(1)上的派生函数时,派生对象已被销毁。

这在我评论(1)和取消注释(2)时运行正常,但我想要做的是自动并且在它被销毁之前总是关闭机器,尽管'断电'动作取决于自定义机器。

有没有更好的方法呢?我应该放弃吗?或者它甚至不是OOP设计的正确方法?

c++ oop design-patterns
2个回答
0
投票

正如您所观察到的,当基类构造函数运行时,派生类部分不再存在。没有诀窍可以做到这一点,所以没有办法在基础析构函数中完成所有操作。

你只需要确定关闭的哪个部分属于每个级别,并让每个析构函数都处理它自己的级别。当对象被销毁时,每个级别的析构函数都是从下到上运行,所以一切都完成了。我认为这将是OOP方法。


0
投票

几点意见:

  1. 如果您可能在析构函数中运行复杂的逻辑,那么通常值得退一步。由于AbstractMachine不知道在powerOffImpl()中将执行什么逻辑,程序员不能确定派生类不会抛出异常,并且析构函数应该总是避免抛出异常。更好的是AirConditioner直接在析构函数中调用powerOffImpl(),然后AirConditioner的实现者知道任何异常风险,并且可以编写代码来缓解这种情况。如果执行此操作没有意义,实现者也可以选择不调用powerOffImpl()
  2. 继承是类之间非常强大的耦合形式。当您将函数实现放在基类中时,您会对派生类应如何操作做出强有力的假设,这在以后很难改变。例如在这种情况下,我们不能尝试两次启动机器。我们能确定总是这样吗?例如当手机已经通电时,按下电源按钮时,我的手机会有所作为。从纯接口(例如IMachine)继承通常更好,以便具体类可以为自己做出这些决定,这也避免了虚方法问题。
  3. 在这里重新考虑模型有时也是一个好主意。 “摧毁”机器意味着什么?当我'破坏'一台真实世界的机器时,它可能无法运行完整的断电序列,那么虚拟机是否应该在析构函数中调用powerOffImpl()?考虑它的另一种方法是考虑机器的寿命可能与开关寿命不同。它们是不同的东西,所以也许它们应该以不同的方式建模。在这种情况下,我会考虑使用单独的MachineSession对象来模拟开关生命周期。 MachineSession构造函数接受IMachine的一个实例并执行IMachine.powerOn(),并在其析构函数中调用IMachine.powerOff()(假设powerOff给出noexcept保证,或者用try / catch保护它)。这也避免了对m_isPoweredOn的需要,因为如果你有一个有效的MachineSession,那么你知道机器已通电。
© www.soinside.com 2019 - 2024. All rights reserved.