Может ли конструктор вернуть значение NULL? - PullRequest
29 голосов
/ 18 мая 2010

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

Ответы [ 15 ]

26 голосов
/ 18 мая 2010

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

static CMyClass* CMyClass::create();

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

21 голосов
/ 18 мая 2010

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

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

9 голосов
/ 18 мая 2010

«Правильный» ** способ - создать исключение.

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

6 голосов
/ 18 мая 2010

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

3 голосов
/ 18 мая 2010

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

1 голос
/ 08 апреля 2014

Это можно сделать немного хакерским путем переопределения нового оператора

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

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

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

1 голос
/ 08 декабря 2011

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

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

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

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

1 голос
/ 18 мая 2010

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

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

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

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

// 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...
}

редактировать

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

1 голос
/ 18 мая 2010

с плохим вкусом.

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

0 голосов
/ 13 июля 2018

Настоящее сообщение должно быть комментарием к ответу, но нет хорошего места для просмотра кода!

Я наткнулся на следующий код:

std::ofstream f(m_filename);
if (!f)
     return false;

и увидел, что он действительно передает оператор return false;. И сказал: «Что? Конструктор вернул NULL?"

Меня вводили в заблуждение, поскольку if делает неявный вызов operator bool() класса ofstream.

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