Я пробовал все альтернативы исключениям, которые я мог найти (переменная переменной члена, даже setjmp/longjmp), и все они сосали свой особый путь. Очень редко шаблон, который я полюбому проходит ссылку на объект ошибки вокруг, и проверка, если ошибка находится на рассмотрении в качестве самой первой операции в любой функции:
int function1(Error& e, char * arg)
{
if(e.failure())
return -1; // a legal, but invalid value
// ...
}
int function2(Error& e, int arg)
{
if(e.failure())
return -1; // a legal, but invalid value
// ...
}
int function3(Error& e, char * arg)
{
if(e.failure())
return -1;
// if function1 fails:
// * function2 will ignore the invalid value returned
// * the error will cascade, making function2 "fail" as well
// * the error will cascade, making function3 "fail" as well
return function2(e, function1(e, arg));
}
С некоторой работой, это относится конструкторам, а также:
class Base1
{
protected:
Base1(Error& e)
{
if(e.failure())
return;
// ...
}
// ...
};
class Base2
{
protected:
Base2(Error& e)
{
if(e.failure())
return;
// ...
}
// ...
};
class Derived: public Base1, public Base2
{
public:
Derived(Error& e): Base1(e), Base2(e)
{
if(e.failure())
return;
...
}
};
основной вопрос вы не получите автоматическое удаление в случае, если конструктор динамически выделяемый объект терпит неудачу. Я типично обернуть инвокации нового в функции следующим образом:
// yes, of course we need to wrap operator new too
void * operator new(Error& e, size_t n)
{
if(e.failure())
return NULL;
void * p = ::operator new(n, std::nothrow_t());
if(p == NULL)
/* set e to "out of memory" error */;
return p;
}
template<class T> T * guard_new(Error& e, T * p)
{
if(e.failure())
{
delete p;
return NULL;
}
return p;
}
, который будет использоваться как это:
Derived o = guard_new(e, new(e) Derived(e));
Преимущества этого метода включают в себя:
- совместимость с C (если класс ошибки объявлен соответствующим образом)
- потолочный сейф
- ze ro накладные расходы в классах
- Класс ошибки может быть непрозрачным на 100%; с использованием макросов для доступа, объявления и передачи его, он может включать в себя все виды информации, включая, но не ограничиваясь, исходный файл и строку, имя функции, обратную трассировку стека и т. д.
- применяется к довольно сложным выражениям, что делает его почти как исключения во многих случаях
Означает ли ваша компания, что исключения присутствуют каждый раз, когда они называются «новыми», или в любое время, когда они используют стандартную библиотеку? – GManNickG
@GMan Мы склонны переопределять оператор new. и я не знаю о проблемах с библиотекой, не могли бы вы рассказать мне немного больше? – sevity
Используйте ключевое слово nothrow в этом случае, чтобы избежать исключения. int * p = new (nothrow) int [100]; // on fail возвращает указатель NULL Но как вы избежите исключения bad_cast, может быть, вам нужно отключить исключение? – Ashish