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