替代基础析构函数上的虚拟函数调用
Alternate to virtual function call on base destructor
所以,我写了下面的代码。
#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设计的正确方式?
正如您所观察到的,当基类构造函数运行时,派生类部分不再存在。 没有技巧可以做到这一点,所以没有办法在基本析构函数中(或从)完成所有操作。
您只需要弄清楚关闭的哪个部分属于每个级别,并让每个析构函数负责自己的级别。 当对象被销毁时,每个关卡的析构函数都会从下到上运行,所以一切都完成了。 我认为这将是 OOP 方法。
几个观察结果:
-
如果您可能在析构函数中运行复杂的逻辑,则通常值得退后一步。由于
AbstractMachine
不知道powerOffImpl()
会执行什么逻辑,程序员不能确定派生类不会抛出异常,析构函数应该始终避免抛出异常。对于AirConditioner
来说,最好直接在其析构函数中调用powerOffImpl()
,然后AirConditioner
的实现者知道任何异常风险,并可以编写代码来缓解这种情况。此外,如果这样做没有意义,实现者可以选择不调用powerOffImpl()
。 -
继承是类之间非常强大的耦合形式。当您将函数实现放在基类中时,您会对派生类应该如何操作做出强有力的假设,这些假设以后很难更改。例如,在这种情况下,我们不能尝试两次打开机器电源。我们能确定情况总是如此吗? 例如,我的手机在已经通电时按下开机按钮时会执行某些操作。通常最好从纯接口继承(例如
IMachine
),以便具体类可以自己做出这些决策,这也避免了虚方法问题。 - 有时在这里重新考虑模型也是一个好主意。"摧毁"机器是什么意思?当我"销毁"一台真实世界的机器时,它可能没有能力运行完整的断电序列,那么虚拟机是否应该在析构函数中调用
powerOffImpl()
?另一种考虑方式是考虑机器的使用寿命可能与开关寿命不同。它们是不同的东西,所以也许它们应该以不同的方式建模。在这种情况下,我会考虑使用一个单独的MachineSession
对象来模拟开关寿命。MachineSession
构造函数获取IMachine
的实例并执行IMachine.powerOn()
,并在其析构函数中调用IMachine.powerOff()
(假设 powerOff 给出了 noexcept 保证,或者你用try
/catch
保护它)。这也避免了m_isPoweredOn
,因为如果您有有效的MachineSession
那么您就知道机器已通电。
- 类型擦除的std::function与虚拟函数调用的开销
- 为什么构造函数的虚拟函数调用有时有效,但其他调用却无效
- 虚拟函数调用的性能作为 for 循环中的上限
- 编译器 虚拟函数调用的优化
- MSVC 在不应该调用时内联虚拟函数调用
- 替代基础析构函数上的虚拟函数调用
- 排除外部错误R6025-纯虚拟函数调用
- 为什么这个虚拟函数调用如此昂贵
- C 从虚拟函数调用静态函数
- C++纯虚拟函数调用的性能可变性
- 使用gcc进行纯虚拟函数调用时发生链接器错误
- 子类中的虚拟函数调用父函数一次
- 虚拟函数调用始终比普通函数调用快.为什么
- 错过了一个虚拟函数调用
- 纯虚拟函数调用错误
- R6025纯虚拟函数调用(从sf::InputStream派生的类)
- 通过中断purevirt.c调试R6025纯虚拟函数调用
- 为什么非虚拟函数调用即使在dynamic_cast失败后仍然成功
- 纯虚拟函数调用有趣的案例
- static_cast如何影响虚拟函数调用