Ответ 1
У вас нет обработчика исключения. Когда это происходит, стандарт говорит, что вызывается std:: terminate, который в свою очередь вызывает прерывание. См. Раздел 14.7 на языке программирования С++, 3-е издание.
Я только начинаю с RAII на С++ и настраиваю небольшой тестовый пример. Либо мой код глубоко запутан, либо RAII не работает! (Я думаю, это первый).
Если я запустил:
#include <exception>
#include <iostream>
class A {
public:
A(int i) { i_ = i; std::cout << "A " << i_ << " constructed" << std::endl; }
~A() { std::cout << "A " << i_ << " destructed" << std::endl; }
private:
int i_;
};
int main(void) {
A a1(1);
A a2(2);
throw std::exception();
return 0;
}
за исключением комментариев, я получаю:
A 1 constructed
A 2 constructed
A 2 destructed
A 1 destructed
как и ожидалось, но за исключением получаю:
A 1 constructed
A 2 constructed
terminate called after throwing an instance of 'std::exception'
what(): std::exception
Aborted
поэтому мои объекты не разрушаются, даже если они выходят за рамки. Разве это не вся основа для RAII.
Указатели и исправления очень оценены!
У вас нет обработчика исключения. Когда это происходит, стандарт говорит, что вызывается std:: terminate, который в свою очередь вызывает прерывание. См. Раздел 14.7 на языке программирования С++, 3-е издание.
Проблема заключается в том, что main
имеет особый статус. Когда оттуда выбрано исключение, стек не может быть осмысленно размотан, приложение просто вызывает std:terminate
.
И тогда это немного соображает, почему переменные не выходят за рамки. Мы фактически не оставили область, в которой они были объявлены. То, что происходит, можно считать эквивалентным этому:
int main(void) {
A a1(1);
A a2(2);
std::terminate();
}
(Я считаю, что это реализация, определяемая, вызываются ли деструкторы в этом случае, поэтому на некоторых платформах она будет работать так, как вы ожидали)
У вас есть чистое исключение в основном, что означает вызов для завершения. Попробуйте следующее:
int main(void)
{
try
{
A a1(1);
A a2(2);
throw std::exception();
return 0;
}
catch(const std::exception & e)
{
return 1;
}
}
Если исключение escapes main(), то это определенная погода, то стека разматывают.
попробовать
int main()
{
try
{
doWork(); // Do you experiment here.
}
catch(...)
{ /*
* By catching here you force the stack to unwind correctly.
*/
throw; // re-throw so exceptions pass to the OS for debugging.
}
}
Как указывали другие, у вас есть неперехваченное исключение, которое вызывает terminate(). Он определен в соответствии с реализацией (см. Пункт 9.3 стандарта 15.3 и пункт 15.5.1 пункта 15), вызываются ли в этом случае деструкторы, и определение в вашей реализации, по-видимому, "Нет, они не будут". (Если terminate() вызывается по какой-либо другой причине, чем бросает исключение, не имеющее обработчик, деструкторы не будут вызываться.)
Ваши объекты A не уничтожаются, потому что вызывается std:: terminate.
std:: terminate вызывается, когда необработанное исключение выходит из основного. Если вы закроете свой код в try/catch (даже если улов просто повторно поднимается), вы увидите поведение, которое ожидаете.
Вы не правильно обрабатываете исключение, поэтому ваше приложение выходит, прежде чем объекты выйдут из области действия.
Я объясню немного больше. Если исключение "пузыри" до основного стека разматывается (редактирование). Даже перемещение кода во вторичную функцию не устранит эту проблему. например:
1 #include <exception>
2 #include <iostream>
3
4 void test();
5
6 class A {
7 public:
8 A(int i) { i_ = i; std::cout << "A " << i_ << " constructed" << std::endl; }
9 ~A() { std::cout << "A " << i_ << " destructed" << std::endl; }
10 private: int i_;
11 };
12
13
14 int main(void) {
15 test();
16 return 0;
17 }
18
19 void test(){
20 A a1(1);
21 A a2(2);
22 throw std::exception();
23 }
Приведенный выше код не будет решить проблему. Единственный способ решить эту проблему - обернуть исключенное исключение в блок try-catch. Это приведет к тому, что исключение не достигнет основного, и прекратите прекращение, которое происходит до того, как объекты выйдут из области видимости.
Другие предложили поместить try/catch внутри main()
, чтобы справиться с этим, что отлично работает. По какой-то причине я нахожу редко используемую "функцию-try-block", чтобы выглядеть лучше, что меня удивляет (я думал, что это будет выглядеть слишком странно). Но я не думаю, что есть реальное преимущество:
int main(void)
try
{
A a1(1);
A a2(2);
throw std::exception();
return 0;
}
catch (...)
{
throw;
}
Пара недостатков заключается в том, что, поскольку он редко используется, многие разработчики получают бросок для цикла, когда видят его, а VC6 зажимает его, если это соображение.
Поскольку исключение не обрабатывается к моменту его достижения main(), это приводит к вызову std:: terminate(), у вас по существу есть эквивалент
int main(void) {
A a1(1);
A a2(2);
exit(1);
}
Деструкторы НЕ гарантируют, что будут вызваны в случаях, когда программа завершается до того, как они выйдут из области видимости. Для другого отверстия в RAII рассмотрим:
int main(void) {
A *a1 = new A(1);
}
Работает следующий код.
#include <exception>
#include <iostream>
class A {
public:
A(int i) { i_ = i; std::cout << "A " << i_ << " constructed" << std::endl; }
~A() { std::cout << "A " << i_ << " destructed" << std::endl; }
private:
int i_;
};
void test() {
A a1(1);
A a2(2);
throw std::exception();
}
int main(void) {
try {
test();
} catch(...) {
}
return 0;
}