Утечки памяти в C ++. Стиль программирования - PullRequest
3 голосов
/ 16 января 2012

Для небольших процедур мы можем предотвратить утечки памяти в случае исключения следующим образом:

proc() {

  //allocate memory for matrix
  try {
  }
  catch {
    //free matrix memory
  }
    ...

  //free matrix memory
}

В случае, если наша процедура более сложная:

proc() {

  //allocate memory for matrix
  try {
  }
  catch {
    //free matrix memory
  }
    ...

  try {
  }
  catch {
    //free matrix memory
  }
    ...

  try {
  }
  catch {

    //free matrix memory
  }
    ...

  //free matrix memory
}

Это выглядит несколько неуклюже,Есть ли лучший способ, лучший стиль программирования для контроля утечек памяти?Насколько я знаю, в C ++ есть auto_ptr, и мы можем разрабатывать процедуры, не заботясь об освобождении памяти.

proc() {

  //allocate auto_ptr
  try {
  }
  catch {

  }
    ...

}

Но, насколько я знаю, auto_ptr не предназначен даже для массивов.Так что в общем случае это неприемлемо.

Ответы [ 4 ]

13 голосов
/ 16 января 2012

auto_ptr не уникальный случай.Это не "auto_ptr или ничего".auto_ptr является одним примером общей идиомы программирования, которая обрабатывает распределение / освобождение ресурсов без утечек.

Эта идиома называется RAII .

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

В случае auto_ptr это делается просто путем сохранения в классе указателя навыделенной памяти, и в деструкторе этого класса вызовите delete для указателя.

Вы можете сделать то же самое с вашими собственными классами RAII, используя их вместо auto_ptr.Но есть и другие типы интеллектуальных указателей, которые предпочтительнее, чем auto_ptr (что на самом деле устарело в C ++ 11).

Это shared_ptr (умный указатель с подсчетом ссылок, который удаляет объект, когда существует последняя ссылка), и scoped_ptr (более безопасный, но ограниченный, эквивалент auto_ptr, который может быть реализован в C ++ 03), и unique_ptr, замена C ++ 11для auto_ptr, который решает проблемы, которые auto_ptr имели.unique_ptr можно безопасно использовать в массивах и стандартных контейнерах.

Поэтому вам не следует использовать auto_ptr, но вам абсолютно необходимо использовать другие типы интеллектуальных указателей и RAII в целом.

8 голосов
/ 16 января 2012

Ни в коем случае не используйте ручное распределение памяти в своем клиентском коде!

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

void proc()
{
    Matrix m1;  // might throw
    // ...
    Matrix m2;  // might also throw
    // ...
}

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

Очень, очень маленький пример класса Matrix:

struct Matrix
{
    Matrix(size_t m, size_t n) : buf(m * n), M(m), N(n) { }
    int & operator(size_t i, size_t j) { return buf[i * N + j]; }
private:
    std::vector<int> buf;
    size_t M;
    size_t N;
};

Я был на самом деле ленив и перенес все действительные динамические данные в std::vector;не нужно изобретать велосипед!(Или просто используйте Boost.MultiArray, в этом отношении.)

3 голосов
/ 16 января 2012

Есть много других умных указателей, например, от Boost , некоторые из которых были включены в новый стандарт C ++.

Тем не менее, для массивов «правильным выбором» было бы просто использовать контейнеры STL, в частности std::vector, если бы вы использовали динамический массив - на современных компиляторах он имеет ту же производительность, что и «обычный» массив.

Имейте в виду, что проблема носит более общий характер, чем память: каждый ресурс, который должен быть получен и освобожден, порождает те же проблемы, и решение C ++ состоит в том, чтобы использовать классы, похожие на смарт-указатели, чтобы обернуть их (приобретая владение в конструкторе уничтожая ресурс в деструкторе); эта идиома широко известна как RAII (Инициализация ресурсов - инициализация).

Обратите внимание, что в коде C ++ с исключениями это кажется единственным практическим способом решения проблемы (поскольку язык не предоставляет блоков finally или using, а каждая инструкция представляет собой потенциальный путь возврата из-за к исключениям).

1 голос
/ 16 января 2012

Вы правы, что автоматические указатели не предназначены для массивов, так как указатели в стиле C могут использоваться для ссылки на массивы.Вот почему существуют другие контейнеры, такие как std::vector, std::deque и т. Д. Вы просто используете std::vector<std::shared_ptr<T> >.

...