Можно ли вручную запустить std:: bad_alloc?
У меня есть этот код.
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?
Ответы
Ответ 1
Вам не нужно это делать. Вы можете использовать беззаметную форму оператора throw
, чтобы поймать исключение std::bad_alloc
, зарегистрировать его, а затем изменить его:
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;
}
Ответ 2
Чтобы ответить на вопрос (так как никто больше не ответил на него), стандарт С++ 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).
Ответ 3
Я лично брошу его, если я использую какой-то пользовательский распределитель в контейнерах STL. Идея состоит в том, чтобы представить тот же интерфейс, в том числе с точки зрения поведения, в библиотеки STL как стандартный std:: allocator.
Итак, если у вас есть пользовательский распределитель (скажем, один выделение из пула памяти) и сбоя основного выделения, вызовите "throw std:: bad_alloc". Это гарантирует, что вызывающий абонент, которому 99,9999% времени является некоторым контейнером STL, будет корректно отображать его. У вас нет контроля над тем, что будут реализованы в тех реализациях STL, если распределитель вернет большое количество жира 0 - вряд ли вам что-то понравится.
Ответ 4
Другой шаблон - использовать тот факт, что логгер тоже подвержен 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
?