Каков наилучший способ выхода из конструктора при условии ошибки в C ++ - PullRequest
20 голосов
/ 10 апреля 2009

Каков наилучший способ выхода из конструктора при условии ошибки в C ++? В частности, это ошибка открытия файла.

Спасибо за ответы. Я бросаю исключение. Вот код (не знаю, если это лучший способ сделать это, но это просто)

// Test to see if file is now open; die otherwise 
if ( !file.is_open() ) {
    cerr << "Failed to open file: " << m_filename << endl;
    throw ("Failed to open file");
}   

Мне кажется, что в C ++ мне нравится то, что вам не нужно объявлять выданные исключения в объявлениях методов.

Ответы [ 8 ]

27 голосов
/ 10 апреля 2009

Лучшее предложение, вероятно, то, что говорит Parashift. Но, пожалуйста, прочитайте мое предупреждение также ниже.

См. Часто задаваемые вопросы по parashift 17.2

[17.2] Как я могу обработать конструктор что терпит неудачу?

Брось исключение.

Конструкторы не имеют возвращаемого типа, так что невозможно использовать возврат коды. Лучший способ подать сигнал отказ конструктора, следовательно, бросить исключение. Если у вас нет возможность использования исключений, «наименее плохой» обходной путь, чтобы поставить объект в состояние "зомби" установить бит внутреннего состояния, чтобы объект действует вроде как он мертв хотя технически это все еще жив.

Идея "зомби" объекта имеет много минусов. Вам нужно добавить функция-член запроса ("инспектора") для проверьте этот бит "зомби", чтобы пользователи ваш класс может узнать, если их объект действительно жив, или если это зомби (то есть, объект "живых мертвецов"), и почти в каждом месте, где вы построить один из ваших объектов (в том числе внутри более крупного объекта или массив объектов) нужно проверить этот флаг состояния через оператор if. Вы также хотите добавить if к вашему другие функции-члены: если объект это зомби, делать не-операции или, возможно, что-то более неприятное.

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


Предостережение с использованием исключений в конструкторе:

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

class B
{
public:
    B()
    {

    }

    virtual ~B()
    {
        //called after D's constructor's exception is called
    }
};

class D : public B
{
public:
    D()
    {
        p = new char[1024];
        throw std::exception("test");
    }

    ~D()
    {
      delete[] p;
      //never called, so p causes a memory leak
    }

    char *p;
};

int main(int argc, char **argv)
{

    B *p;
    try
    {
        p = new D();
    }
    catch(...)
    {

    }


    return 0;
}

Защищенные / частные конструкторы с методом CreateInstance:

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

7 голосов
/ 10 апреля 2009

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

Чтобы создать конструктор, который не может завершиться сбоем, рефакторинг кода, который потенциально может завершиться сбоем, в метод init(), и пусть конструктор выполнит как можно меньше работы, а затем потребует от всех пользователей класса вызова init() сразу после строительства. Если init() не удалось, вы можете вернуть код ошибки. Обязательно документируйте это в документации вашего класса!

Конечно, это несколько опасно, поскольку программисты могут забыть вызвать init(). Компилятор не может обеспечить это, поэтому действуйте осторожно и постарайтесь, чтобы ваш код работал быстро, если init() не вызывается.

4 голосов
/ 10 апреля 2009

В общем, вы должны бросить исключение. Альтернатива состоит в том, чтобы иметь какой-то наполовину правильно построенный объект, который пользователь должен каким-то образом протестировать, что он неизбежно потерпит неудачей.

2 голосов
/ 10 апреля 2009

Если объект, который вы создаете, является недопустимым из-за ошибки и должен быть утилизирован вызывающей стороной, то вам, скорее всего, придется сгенерировать исключение. Это позволяет компилятору выполнять правильное освобождение ресурсов.

(Написание безопасных для исключения конструкторов требует некоторой осторожности - вкратце, вам нужно использовать списки инициализаторов везде, где это возможно, а не использовать тело конструктора - но очень важно, если у вас есть такой случай, где исключение является значительной возможностью.)

1 голос
/ 10 апреля 2009

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

0 голосов
/ 10 апреля 2009

Лучше всего сделать исключение. Вот для чего они здесь, и любая попытка повторить поведение, которое вы получаете, может где-то провалиться.

Если по какой-то причине вы не можете использовать исключение, используйте nothrow. Пример в Стандарте 18.4.1.1, пункт 9:

t* p2 = new(nothrow) T;    // returns 0 if it fails

Технически это новая форма размещения, но она должна либо возвращать полностью сформированный объект, либо нулевой указатель, который нужно проверить, за исключением того, что никто не будет.

Если у вашего класса может быть объект, который существует, но не инициализирован должным образом, у вас может быть элемент данных, который служит в качестве флага относительно того, является ли класс полезным или нет. Опять же, никто не будет проверять этот флаг в живом коде.

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

Действительно, исключения - лучший путь.

0 голосов
/ 10 апреля 2009

Есть только 1 хороший способ выйти из конструктора, который содержит ошибку, то есть вызвать исключение.

Это действительно ошибка? Вы пытаетесь добавить слишком много в конструктор?

Часто люди пытаются ввести какое-то начальное взаимодействие в конструктор, например, добавив имя файла в конструктор файла. Ожидаете ли вы, что он сразу же откроет этот файл, или вы просто устанавливаете какое-то состояние, отличается ли оно от file.open (имя файла), это нормально, если это не удается?

0 голосов
/ 10 апреля 2009

Брось исключение. Посмотрите здесь для получения дополнительной информации: Ошибка обработки в конструкторе

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