处理关键对象构造函数失败的好方法

时间:2010-05-02 22:40:17

标签: c++ exception constructor

我正在尝试在两种实例化对象和方式之间做出决定。处理对我的程序至关重要的对象的任何构造函数异常,即如果构造失败,则程序无法继续。

我有一个包含基本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哲学不是吗?

我仍然是这方面的新手所以任何对上述内容的反馈都非常感谢。如果有更好的方法来检查/处理构造函数失败,请告诉我。

2 个答案:

答案 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块中的事实可以解决为重构为小方法。