безопасность исключений c ++ - PullRequest
6 голосов
/ 05 августа 2011

В исключительном C ++ упоминается следующий код

template <class T> class Stack 
{
    public:
      Stack();
      ~Stack();

      /*...*/

    private:
       T*     v_;      // ptr to a memory area big
       size_t vsize_;  //  enough for 'vsize_' T's
       size_t vused_;  // # of T's actually in use
};



template<class T> 
Stack<T>::Stack()
        : v_(new T[10]),  // default allocation
          vsize_(10),
          vused_(0)       // nothing used yet
{ 
}

В нем говорится, что если один из конструкторов T сгенерировал, то все полностью построенные объекты T были должным образом уничтожены, и, наконец, оператор удаления был автоматическиосвободить память.Это делает нас защищенными от утечек.

Насколько я понимаю, если конструктор выдает исключение, приложение должно очистить все выделенные ресурсы.Насколько выше герметичность?

Ответы [ 3 ]

8 голосов
/ 05 августа 2011

Цитирование стандарта C ++ 03, §5.3.4 / 8:

A new-expression получает хранилище для объекта, вызывая функцию выделения .Если new-expression завершается выдачей исключения, оно может освободить хранилище, вызвав функцию освобождения.Если назначенный тип не является типом массива, имя функции распределения - operator new, а имя функции освобождения - operator delete.Если выделенный тип является типом массива, имя функции распределения будет operator new[], а имя функции освобождения - operator delete[].

§5.3.4 / 17:

Если какая-либо часть инициализации объекта, описанного выше, завершается выдачей исключения и может быть найдена подходящая функция освобождения, функция освобождения вызывается для освобождения памяти, в которой создавался объект, после чего исключение продолжает распространяться вконтекст new-expression .

Следовательно, если какой-либо конструктор T выдает исключение, среда выполнения уничтожит все уже созданные подобъекты экземпляра Tчей конструктор сгенерировал, затем вызвать operator delete[] для массива в целом, уничтожив все уже созданные элементы и освободив память массива.

3 голосов
/ 05 августа 2011

[Исправление:] Это не так. Исключение в конструкторе не приведет к утечке ресурсов, поскольку единственное место, где может возникнуть исключение, находится внутри выражения new, а если выражение newпроисходит сбой, ресурсы, выделенные им, освобождаются. Ваша ситуация особенная, потому что вы делаете только одно выделение в конструкторе - в общем, это небезопасно!

Обозначенная вами цитата - это оператор удаления для объекта с ошибкой, чейКонструктор Threw:

struct T
{
  T() { throw 1; }
  char data[200];
};

// in your code:

T * pt = new T;

В последней строке память выделяется до вызова конструктора. Что память освобождается в случае исключения автоматическим вызовом ::operator delete(pt).(Как правило, вызывается соответствующий оператор удаления (не «выражение»!), Соответствующий новому выражению.)

Это выглядит так:

  • Успешное построение:1. Распределение.2. Строительство.3. Уничтожение.4. Распределение.

  • Неудачное построение: 1. Распределение.2. Распределение.

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

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

class Danger
{
  T * pt1, * pt2;
public:
  Danger()
  {
    try { pt1 = new T; } catch(...) { throw(); }
    try { pt2 = new T; } catch(...) { delete pt1; throw(); }
  }
};

Или, по принципу единой ответственности, не используйте необработанные указатели, а используйте контейнеры для управления ресурсамичто убирать за собой !!

0 голосов
/ 05 августа 2011

Попробуйте автоматический указатель для T* v_ или любых динамически распределенных ресурсов.Если произойдет следующее

template<class T> 
Stack<T>::Stack()
        : v_(new T[10]),
          vsize_(10),
          vused_(0)
{
    throw 0; // terminated by exception
}

или в Stack при создании исключения будет другой объект, v_ приведет к утечке памяти.Если вы оберните его как std::unique_ptr<T[]> v_ или что-то в этом роде, оно будет автоматически освобождено, если конструкция Stack завершится из-за исключения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...