C ++ RAII не работает? - PullRequest
       11

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

13 голосов
/ 09 июля 2009

Я только начинаю работать с RAII в C ++ и настраиваю небольшой тестовый пример. Либо мой код сильно сбит с толку, либо 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

так что мои объекты не разрушаются, даже если они выходят за рамки видимости. Разве это не вся основа для РАИИ.

Указатели и исправления приветствуются!

Ответы [ 10 ]

20 голосов
/ 09 июля 2009

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

17 голосов
/ 09 июля 2009

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

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

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

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

6 голосов
/ 09 июля 2009

Если исключение выходит из 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.
    }
}
6 голосов
/ 09 июля 2009

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

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


}
5 голосов
/ 09 июля 2009

Как уже отмечали другие, у вас есть неперехваченное исключение, которое вызывает terminate (). Это зависит от реализации (см. Стандарт, 15.3, параграф 9 и 15.5.1, параграф 2), вызывать деструкторы в этом случае, и определение в вашей реализации, по-видимому, «Нет, они не будут». (Если terminate () вызывается по любой другой причине, кроме выдачи исключения, которое не имеет обработчика, деструкторы вызываться не будут.)

4 голосов
/ 09 июля 2009

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

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

3 голосов
/ 09 июля 2009

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

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

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

1 голос
/ 10 июля 2009

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

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

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

0 голосов
/ 05 февраля 2010

Следующий код работает.

#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; 
} 
0 голосов
/ 09 июля 2009

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

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

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

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