С++ RAII не работает?

Я только начинаю с 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.

Указатели и исправления очень оценены!

Ответ 1

У вас нет обработчика исключения. Когда это происходит, стандарт говорит, что вызывается std:: terminate, который в свою очередь вызывает прерывание. См. Раздел 14.7 на языке программирования С++, 3-е издание.

Ответ 2

Проблема заключается в том, что main имеет особый статус. Когда оттуда выбрано исключение, стек не может быть осмысленно размотан, приложение просто вызывает std:terminate.

И тогда это немного соображает, почему переменные не выходят за рамки. Мы фактически не оставили область, в которой они были объявлены. То, что происходит, можно считать эквивалентным этому:

int main(void) {
  A a1(1);
  A a2(2);
  std::terminate();
}

(Я считаю, что это реализация, определяемая, вызываются ли деструкторы в этом случае, поэтому на некоторых платформах она будет работать так, как вы ожидали)

Ответ 3

У вас есть чистое исключение в основном, что означает вызов для завершения. Попробуйте следующее:

int main(void)
{
    try
    {
        A a1(1);
        A a2(2);
        throw std::exception();
        return 0;
    }
    catch(const std::exception & e)
    {
        return 1;
    }


}

Ответ 4

Если исключение 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.
    }
}

Ответ 5

Как указывали другие, у вас есть неперехваченное исключение, которое вызывает terminate(). Он определен в соответствии с реализацией (см. Пункт 9.3 стандарта 15.3 и пункт 15.5.1 пункта 15), вызываются ли в этом случае деструкторы, и определение в вашей реализации, по-видимому, "Нет, они не будут". (Если terminate() вызывается по какой-либо другой причине, чем бросает исключение, не имеющее обработчик, деструкторы не будут вызываться.)

Ответ 6

Ваши объекты A не уничтожаются, потому что вызывается std:: terminate.

std:: terminate вызывается, когда необработанное исключение выходит из основного. Если вы закроете свой код в try/catch (даже если улов просто повторно поднимается), вы увидите поведение, которое ожидаете.

Ответ 7

Вы не правильно обрабатываете исключение, поэтому ваше приложение выходит, прежде чем объекты выйдут из области действия.

Я объясню немного больше. Если исключение "пузыри" до основного стека разматывается (редактирование). Даже перемещение кода во вторичную функцию не устранит эту проблему. например:

      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. Это приведет к тому, что исключение не достигнет основного, и прекратите прекращение, которое происходит до того, как объекты выйдут из области видимости.

Ответ 8

Другие предложили поместить try/catch внутри main(), чтобы справиться с этим, что отлично работает. По какой-то причине я нахожу редко используемую "функцию-try-block", чтобы выглядеть лучше, что меня удивляет (я думал, что это будет выглядеть слишком странно). Но я не думаю, что есть реальное преимущество:

int main(void) 
try
{
    A a1(1);
    A a2(2);
    throw std::exception();
    return 0;
}
catch (...) 
{
    throw;
}

Пара недостатков заключается в том, что, поскольку он редко используется, многие разработчики получают бросок для цикла, когда видят его, а VC6 зажимает его, если это соображение.

Ответ 9

Поскольку исключение не обрабатывается к моменту его достижения main(), это приводит к вызову std:: terminate(), у вас по существу есть эквивалент

int main(void) {
  A a1(1);
  A a2(2);
  exit(1);
}

Деструкторы НЕ гарантируют, что будут вызваны в случаях, когда программа завершается до того, как они выйдут из области видимости. Для другого отверстия в RAII рассмотрим:

int main(void) {
  A *a1 = new A(1);
}

Ответ 10

Работает следующий код.

#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; 
}