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

MTL*_*hil 5 c++ constructor exception

我试图在两种实例化对象和处理对我的程序至关重要的对象的构造函数异常之间做出决定,即如果构造失败,程序就无法继续.

我有一个包含基本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;   
}
Run Code Online (Sandbox Code Playgroud)

方法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;

}
Run Code Online (Sandbox Code Playgroud)

我更喜欢方法2中的代码外观,不必将整个程序插入到try块中,但是方法1在堆栈上创建对象,因此C++管理对象的生命周期,这与RAII更加一致哲学不是吗?

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

raz*_*ebe 1

就我个人而言,我更喜欢您使用的第一种样式- 方法 1 - 将SimpleMIDIOut对象分配为 try-catch 块范围的本地对象。

  • 对我来说,try-catch 块的好处之一是它为错误处理代码(catch 块)提供了一个整洁的地方,它允许您在一个漂亮、可读、不间断的流程中指定业务逻辑。

  • 其次,您指定的 try-catch 块足够通用,足以处理由此派生的任何异常std::exception- 因此,将大量程序代码放在 try-catch 块中并不是一件坏事。如果发生最坏的情况并抛出异常,它将防止您的程序意外终止。这可能是您意想不到的异常,例如索引越界或内存分配异常。

  • 如果出于可读性原因您不喜欢在 try-catch 块中包含大量代码,那也没关系,因为将大块代码重构为函数是很好的设计实践。通过这样做,您可以将主函数本身的行数保持在最少。

查看方法2:

  • 你不需要delete在 catch 块中使用它。如果在构造过程中抛出异常,那么无论如何都没有可以删除的对象。

  • 您是否考虑过计划如何应对std::exception可能抛出的任何问题myOut->PlayNote?它超出了 try-catch 的范围,因此这里的异常会意外终止程序。这就是我上面第二个要点的意思。

如果您决定将大部分程序包装在该 try-catch 块中,但仍希望动态分配对象,则可以使用auto_ptr在事件中为您管理内存,SimpleMIDIOut从而使内存管理变得更容易异常情况:

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;
Run Code Online (Sandbox Code Playgroud)

...但您也可以将SimpleMIDIOut对象创建为本地对象而不是动态对象。