Подтвердить что ты не робот

Может ли конструктор вернуть значение NULL?

Я знаю, что конструкторы не "возвращают" что-либо, но, например, если я вызываю CMyClass *object = new CMyClass(), есть ли способ сделать объект NULL, если конструктор завершится с ошибкой? В моем случае у меня есть некоторые изображения, которые нужно загрузить, и если чтение файла не удастся, я бы хотел, чтобы он возвращал значение null. Есть ли способ сделать это?
Спасибо заранее.

4b9b3361

Ответ 1

Я согласен со всеми остальными, что вы должны использовать исключения, но если вам действительно нужно использовать NULL по какой-либо причине, сделайте конструктор закрытым и используйте метод factory:

static CMyClass* CMyClass::create();

Это означает, что вы не можете нормально создавать экземпляры, и вы больше не можете их размещать в стеке, что является довольно большим недостатком

Ответ 2

Конструкторы не возвращают значения. Они инициализируют объект, и единственный способ сообщения об ошибках - это исключение.

Обратите внимание, что конструктор не управляет каким-либо типом управления памятью. Память выделяется извне, а затем для ее инициализации вызывается конструктор. И эта память может быть динамически распределена (type *x = new type;), но она также может быть в стеке (type x;) или подобъекте более сложного типа. Во всех случаях, кроме первого, null не имеет никакого смысла.

Ответ 3

"Правильный" путь - это исключение.

** Вы можете предоставить функцию-член как is_valid, которую вы можете проверить после создания объекта, но это не является идиоматическим в С++.

Ответ 4

Способ сделать это, если вы обнаружите что-то, что не работает в вашем конструкторе, вы должны выбросить исключение. Это то, что происходит, если С++ не может выделить память для вашего объекта - он выдает std:: bad_alloc. Вы должны использовать std:: exception или подкласс.

Ответ 5

Может ли использовать статический метод factory? При конвертации между типами я могу создать публичный статический CMyClass Convert (оригинал) и вернуть null, если оригинал имеет значение NULL. Вероятно, вы все же хотите генерировать исключения для недопустимых данных.

Ответ 6

В плохом вкусе.

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

Ответ 7

Вместо того, чтобы сообщать вам, как получить конструктор для возврата null или как его подделать, позвольте мне предложить альтернативу: предложить способ избежать исключения исключения, например, с задержкой инициализации или конструктором без метаданных. Однако, если вы это сделаете, вам нужно иметь возможность проверить правильность и убедиться, что любая попытка использовать недопустимый экземпляр делает исключение. Другими словами, вы задерживаете исключение, не избегая его полностью.

Вот как: У вас уже есть конструктор, который принимает путь к файлу и загружает его, бросая неудачу. Переместите кишки в метод Load, который принимает путь к файлу и возвращает bool для указания успеха. Затем измените конструктор, чтобы он просто вызывал Load и выбрасывал false. В Load обязательно верните false, если экземпляр правильно инициализирован. Затем добавьте деструктор по умолчанию и метод IsValid.

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

Это дает вам все, о чем вы можете попросить, без создания недостижимого кода. Он должен выглядеть примерно так:

// Per Dennis, should go away if Load becomes private.
Image()
{
    _valid = false;
}

Image(const string& filepath)
{
    if (!Load(filepath))
        throw new exception("Cannot open image.");
}

// Per Dennis.
Image(const string& filepath, bool doThrow)
{
    if (!Load(filepath) && doThrow)
        throw new exception("Cannot open image.");
}

// Per Dennis, this should probably be made private now.
bool Load(const string& filepath)
{
    if (_valid)
        return false;

    // Try to load...
    _valid = WhetherItLoadedExpression;
    return _valid;
}

bool IsValid()
{
    return _valid;
}

void Draw()
{
    if (!IsValid())
        throw new exception("Invalid object.");

    // Draw...
}

изменить

См. ниже изменения, внесенные в ответ на комментарий Денниса.

Ответ 8

Это можно сделать немного взломанным, переопределив новый оператор

См. этот пример:

http://coliru.stacked-crooked.com/a/62e097827724f91e

Технически это уже не конструктор, но он ведет себя так, как вы этого хотите.

Ответ 9

В Visual С++ 6 поведение по умолчанию для голодания в памяти было для нового оператора, который возвращает NULL, а не генерирует исключение. Это не было поведение, позднее стандартизованное в С++, а также идиоматическое в современном С++.

Но вы, безусловно, можете создать версию оператора new, которая ведет себя таким образом, если вы этого пожелаете, или используйте вариант nothrow: if ( Foo * foo = new ( std::nothrow ) Foo ) { ... }.

Ответ 10

Фактически вы можете заставить new "возвращать" 0, используя std:: nothrow, но это только заставляет его возвращать 0, если сбой памяти не выполняется. Как только он попадет в ваш конструктор, вы не сможете получить то, что хотите.

Вы должны разделить проблемы в своем классе. Конструктор должен почти никогда (я испытываю соблазн сказать "никогда", но я оставлю место для редкого исключения, о котором я не могу думать) обрабатывать файлы, если только обработка файлов является его исключительной ответственностью (например, fstream).

Ответ 11

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

Внимание:

malloc не вызывает конструктор объекта.

Ответ 12

Исключения - лучший выбор.

Вы также можете проверить значение errno, если вы программируете в среде Unix.

Ответ 13

если я вызываю CMyClass* object = new CMyClass(), есть ли способ сделать объект равным NULL, если конструктор не работает?

Я понимаю, что вы имеете в виду! Существует множество библиотек С++, которые активно используют динамически распределенную память и реализуют эту идею (например, QtGstreamer), поэтому ее определенно можно напишите свой код, чтобы он выглядел следующим образом:

CMyClass* object = new CMyClass()
if (!object)
{
    // FAILED!
}

Однако не является конструктором объекта, который возвращает NULL. Это перегруженная версия operator new.

Ответ 14

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