所以,我已经写了下面的代码。
#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)时,这样运行正常,但我想要做的是自动并且在机器被销毁之前总是关闭机器,尽管关闭电源&#39 #39;操作取决于自定义计算机。
有更好的方法吗? 我应该放弃吗? 或者它甚至不是OOP设计的正确方法?
答案 0 :(得分:0)
正如您所观察到的,当基类构造函数运行时,派生类部分不再存在。没有诀窍可以做到这一点,所以没有办法在基础析构函数中完成所有操作。
你只需要确定关闭的哪个部分属于每个级别,并让每个析构函数处理它自己的级别。当对象被销毁时,每个级别的析构函数都是从下到上运行,所以一切都完成了。我认为这将是OOP方法。
答案 1 :(得分:0)
有几点意见:
如果您可能在析构函数中运行复杂的逻辑,那么通常值得退一步。由于AbstractMachine
不知道在powerOffImpl()
中将执行什么逻辑,程序员不能确定派生类不会抛出异常,并且析构函数应该始终避免抛出异常。最好AirConditioner
直接在析构函数中调用powerOffImpl()
,然后AirConditioner
的实现者知道任何异常风险,并且可以编写代码来缓解这种情况。如果执行此操作没有意义,实现者也可以选择不调用powerOffImpl()
。
继承是类之间非常强大的耦合形式。当您将函数实现放在基类中时,您会对派生类应如何操作做出强有力的假设,这在以后很难改变。例如在这种情况下,我们不能尝试两次启动机器。我们能确定总是这样吗?例如当手机已经通电时,按下电源按钮时,我的手机会有所作为。从纯接口(例如IMachine
)继承通常更好,这样具体的类可以为自己做出这些决定,这也避免了虚方法问题。
powerOffImpl()
?考虑它的另一种方法是考虑机器的寿命可能与开关寿命不同。它们是不同的东西,所以也许它们应该以不同的方式建模。在这种情况下,我会考虑使用单独的MachineSession
对象来模拟开关生命周期。 MachineSession
构造函数接受IMachine
的实例并执行IMachine.powerOn()
,并在其析构函数中调用IMachine.powerOff()
(假设powerOff给出noexcept保证,或者用{{{{}保护它。 1}} / try
)。这也避免了catch
的需要,因为如果您有一个有效的m_isPoweredOn
,那么您就知道机器已通电。