Это нормально, чтобы вручную бросить std :: bad_alloc?
-
08-10-2019 - |
Вопрос
У меня есть этот код ..
CEngineLayer::CEngineLayer(void)
{
// Incoming creation of layers. Wrapping all of this in a try/catch block is
// not helpful if logging of errors will happen.
logger = new (std::nothrow) CLogger(this);
if(logger == 0)
{
std::bad_alloc exception;
throw exception;
}
videoLayer = new (std::nothrow) CVideoLayer(this);
if(videoLayer == 0)
{
logger->log("Unable to create the video layer!");
std::bad_alloc exception;
throw exception;
}
}
IEngineLayer* createEngineLayer(void)
{
// Using std::nothrow would be a bad idea here as catching things thrown
// from the constructor is needed.
try
{
CEngineLayer* newLayer = new CEngineLayer;
return (IEngineLayer*)newLayer;
}
catch(std::bad_alloc& exception)
{
// Couldn't allocate enough memory for the engine layer.
return 0;
}
}
Я опущел большую часть не связанной информации, но я думаю, что картинка здесь ясно.
Это нормально, чтобы вручную бросить std :: bad_alloc вместо того, чтобы попробовать / ловить все творения слоев индивидуально и входить в лесозаготовку перед вентиляцией bad_allocs?
Решение
Вам не нужно это делать. Вы можете использовать форму без параметра throw
заявление, чтобы поймать std::bad_alloc
Исключение, войти в систему, затем Rethrow это:
logger = new CLogger(this);
try {
videoLayer = new CVideoLayer(this);
} catch (std::bad_alloc&) {
logger->log("Not enough memory to create the video layer.");
throw;
}
Или если logger
не умный указатель (который это должно быть):
logger = new CLogger(this);
try {
videoLayer = new CVideoLayer(this);
} catch (std::bad_alloc&) {
logger->log("Not enough memory to create the video layer.");
delete logger;
throw;
} catch (...) {
delete logger;
throw;
}
Другие советы
Просто чтобы ответить на вопрос (так как никто другой, кажется, не ответил на него), стандарт C ++ 03 определяет std::bad_alloc
следующим образом:
namespace std {
class bad_alloc : public exception {
public:
bad_alloc() throw();
bad_alloc(const bad_alloc&) throw();
bad_alloc& operator=(const bad_alloc&) throw();
virtual ˜bad_alloc() throw();
virtual const char* what() const throw();
};
}
Поскольку стандарт определяет общественный конструктор, вы бы идеально в безопасности построить и бросить один из вашего кода. (Любой объект с конструктором публичных копий можно бросить, IIRC).
Я лично бросаю это, если я использую какой-то пользовательский распределитель в контейнерах STL. Идея состоит в том, чтобы представить тот же интерфейс, в том числе с точки зрения поведения - к библиотекам STL как по умолчанию STD :: allocator.
Итак, если у вас есть пользовательский распределитель (скажем, один, выделяющий из пула памяти), а базовый распределитель не удается, позвоните «бросить std :: bad_alloc». Это гарантирует абонент, который 99,99999% времени - это какой-то контейнер STL, приведет его должным образом. Вы не контролируете то, что эти реализации STL сделают, если распределитель возвращает большой жир 0 - вряд ли будет все, что вам понравится.
Другой шаблон состоит в том, чтобы использовать тот факт, что регистратор подчиняется Raii тоже:
CEngineLayer::CEngineLayer( )
{
CLogger logger(this); // Could throw, but no harm if it does.
logger.SetIntent("Creating the video layer!");
videoLayer = new CVideoLayer(this);
logger.SetSucceeded(); // resets intent, so CLogger::~CLogger() is silent.
}
Это масштабируется чисто, если есть несколько шагов. Вы просто звоните .SetIntent
неоднократно. Обычно вы только выписываете последнюю намеренную строку в CLogger::~CLogger()
Но для лишних весовых веществ вы можете записать все намерения.
Кстати, в вашем createEngineLayer
Возможно, вы захотите catch(...)
. Отказ Что если регистратор бросает DiskFullException
?