Хороший стиль для обработки ошибок конструктора критического объекта - PullRequest
5 голосов
/ 03 мая 2010

Я пытаюсь выбрать между двумя способами создания объекта и обработкой любых исключений конструктора для объекта, который критичен для моей программы, т. Е. В случае сбоя конструкции программа не может продолжиться.

У меня есть класс SimpleMIDIOut, который оборачивает основные функции Win32 MIDI. Он откроет 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 ]

2 голосов
/ 03 мая 2010

Лично я предпочитаю первый стиль, который вы использовали - Метод 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, но все же хотели бы динамически выделить объект SimpleMIDIOut, вы могли бы немного упростить управление памятью, используя auto_ptr для управления памятью для вас в случае исключения:

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 голос
/ 03 мая 2010

Можете ли вы уточнить, имеете ли вы контроль над исходным кодом в SimpleMIDIOut?

Если вы делаете это, класс не должен выдавать исключение из CTOR. Код в CTOR этого класса должен быть заключен в блок try \ catch.

Если вы этого не сделаете, то я пойду с методом 1 для ясности - тот факт, что вся программа должна быть в этом блоке try, может быть решена путем рефакторинга в маленькие методы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...