我正在尝试在两种实例化对象和方式之间做出决定。处理对我的程序至关重要的对象的任何构造函数异常,即如果构造失败,则程序无法继续。
我有一个包含基本Win32 MIDI功能的SimpleMIDIOut类。它将在构造函数中打开一个MIDI设备,并在析构函数中将其关闭。如果无法打开MIDI设备,它将在构造函数中抛出从std :: exception继承的异常。
以下哪种捕获此对象的构造函数异常的方法更符合C ++最佳实践
方法1 - 堆栈分配的对象,仅在try块
内的范围内#include <iostream>
#include "simplemidiout.h"
int main()
{
try
{
SimpleMIDIOut myOut; //constructor will throw if MIDI device cannot be opened
myOut.PlayNote(60,100);
//.....
//myOut goes out of scope outside this block
//so basically the whole program has to be inside
//this block.
//On the plus side, it's on the stack so
//destructor that handles object cleanup
//is called automatically, more inline with RAII idiom?
}
catch(const std::exception& e)
{
std::cout << e.what() << std::endl;
std::cin.ignore();
return 1;
}
std::cin.ignore();
return 0;
}
方法2 - 指向对象的指针,分配的堆,更好的结构化代码?
#include <iostream>
#include "simplemidiout.h"
int main()
{
SimpleMIDIOut *myOut;
try
{
myOut = new SimpleMIDIOut();
}
catch(const std::exception& e)
{
std::cout << e.what() << std::endl;
delete myOut;
return 1;
}
myOut->PlayNote(60,100);
std::cin.ignore();
delete myOut;
return 0;
}
我更喜欢方法2中代码的外观,不必将整个程序堵塞到try块中,但是方法1在堆栈上创建了对象,因此C ++管理对象的生命周期,更多的是调整RAII哲学不是吗?
我仍然是这方面的新手所以任何对上述内容的反馈都非常感谢。如果有更好的方法来检查/处理构造函数失败,请告诉我。
答案 0 :(得分:2)
就个人而言,我更喜欢你使用过的第一种风格 - 方法1 - 将SimpleMIDIOut
对象作为本地分配给try-catch块的范围。
对我来说,try-catch块的好处之一就是为错误处理代码提供了一个整洁,整洁的地方 - catch块 - 允许你在一个不错的时候指定你的业务逻辑,可读,不间断的流程。
其次,你指定的try-catch块足够通用,可以处理从std::exception
派生的任何异常 - 所以在try-catch块中有大量的程序代码是'这是一件坏事。如果最坏的情况发生并且抛出异常,它将阻止程序意外终止。可能这可能是您不期望的异常,例如索引超出范围或内存分配异常。
如果您因为可读性原因而不喜欢在try-catch块中拥有大量代码,那没关系,因为refactor big lumps of code into functions是一个很好的设计实践。通过这样做,您可以将主函数本身保持为最少的行数。
查看方法2:
您在catch块中不需要delete
。如果在构造期间抛出了异常,那么无论如何都没有删除对象。
您是否考虑过如何计划std::exception
可能引发的任何myOut->PlayNote
?它超出了try-catch的范围,因此这里的异常会意外地终止程序。这就是我上面的第二颗子弹所得到的。
如果您决定将大部分程序包装在try-catch块中,但仍希望动态分配SimpleMIDIOut
对象,则可以通过使用{{{{}}来简化内存管理。 3}}在发生异常时为您管理内存:
try
{
std::auto_ptr<SimpleMIDIOut> myOut(new SimpleMIDIOut());
myOut->PlayNote(60,100);
std::cin.ignore();
} // myOut goes out of scope, SimpleMIDIOut object deleted
catch(const std::exception& e)
{
std::cout << e.what() << std::endl;
return 1;
}
return 0;
...但您也可以将SimpleMIDIOut
对象创建为本地而不是动态。
答案 1 :(得分:1)
您能否澄清一下您是否可以控制SimpleMIDIOut中的源代码?
如果你这样做,这个课不应该从CTOR中抛出异常。该类的CTOR中的代码应该用try \ catch块包装。
如果你不这样做,那么为了清楚起见我会使用方法1 - 整个程序必须在这个try块中的事实可以解决为重构为小方法。