ПроблемаУтечка ресурсов, вызванная исключением, происходит, когда функция повышает необработанное исключение при помощи throw
оператор, но не освобождает ресурсы, которые были выделены перед исключением.
РискКогда функция повышает необработанное исключение, она сразу выходит из осциллографа. Если функция управляет ресурсами, и они не освобождены до повышения исключения, ресурс пропущен. Рассмотрите этот код:
FILE* FilePtr;
//...
void foo(){
FilePtr = fopen("some_file.txt", "r");
//...
if(/*error condition*/)
throw ERROR_CODE;
//...
fclose(FilePtr);
}
Выделенный указатель файла предназначается, чтобы быть освобожденным перед функциональным выполнением концов. Когда исключение происходит, функциональные выходы, не удаляя указатель, который приводит к утечке ресурсов.
ИсправлениеЧтобы зафиксировать этот дефект, функция должна установить все ресурсы, которые это выделяет допустимому состоянию, прежде чем это выйдет из осциллографа. В предыдущем примере кода функция должна удалить указатель FilePtr
перед throw
оператор.
Вместо того, чтобы вручную отследить выделение и освобождение ресурсов, лучшая практика должна следовать или за Захватом ресурса является инициализацией (RAII) или за Конструктором, Получает, Релизы Деструктора (КАДРЫ) шаблоны разработки. Распределение ресурсов выполняется в конструкторах, и освобождение ресурса выполняется в деструкторах. Жизненным циклом ресурсов управляют ограниченные осциллографом объекты. Когда функции достигают конца своего осциллографа, полученные средства правильно высвобождены. Рассмотрите этот код:
void releaseFile(std::FILE* fp) { std::fclose(fp); }
std::unique_ptr<std::FILE, decltype(&releaseFile)> FilePtr;
//...
void foo(){
FilePtr(std::fopen("some_file.txt"),&releaseFile);
//...
if(/*error condition*/)
throw ERROR_CODE;
}
Уникальный указатель
FilePTR
вызывает функциональный
releaseFile
удалить выделенный ресурс однажды функциональный
foo
достигает конца его осциллографа. Выходит ли функция обычно с необработанным исключением, выделенные ресурсы освобождены.
Интеллектуальные указатели C++, такие как std::unique_ptr
и std::shared_ptr
следуйте за шаблоном RAII. Они упрощают управление жизненный цикл ресурсов во время обработки исключений. Каждый раз, когда возможно, избегайте использования необработанных указателей.
Пример — утечка ресурсов, вызванная исключением#include <cstdint>
#include <memory>
#include <stdexcept>
extern int sensorFlag() noexcept;
namespace Noncompliant{
void func(){
int* intPtr = new int;
int data = sensorFlag();
if(data==-1)//Error
throw std::runtime_error("Unexpected value");//Noncompliant
//...
delete intPtr;
}
}
В этом примере, функциональном Noncompliant::func()
управляет необработанным указателем inPtr
. Функция выделяет память для него, и затем выпускает память после некоторых операций. Функция выходит за исключением когда data
-1
. В этом случае, функциональные выходы прежде, чем выпустить выделенную память, приводя к утечке памяти. Polyspace® отмечает throw
оператор.
Коррекция — освобождает ресурсы перед throw
ОператорыЧтобы предотвратить утечку памяти, выделенная память должна быть выпущена прежде, чем повысить исключение, как показано в Compliant::func
.
Лучшая практика должна следовать шаблону разработки RAII. Например, когда C++ 14 доступен, используйте unique_ptr
вместо необработанного указателя. BestPractice::func
показывает реализацию func
это следует за шаблоном RAII. Жизненный цикл памяти управляем самим объектом. Таким образом, однажды func
вне осциллографа, интеллектуальный указатель intPtr
удаляет себя и выпускает память. Поскольку управление памятью выполняется правильно интеллектуальным указателем, BestPractice::func
более просто и более безопасен.
#include <cstdint>
#include <memory>
#include <stdexcept>
extern int sensorFlag() noexcept;
namespace Compliant{
void func(){
int* intPtr = new int;
int data = sensorFlag();
if(data==-1){//Error
delete intPtr;
throw std::runtime_error("Unexpected value");//Compliant
}
//...
delete intPtr;
}
}
namespace BestPractice{// C++14
void func(){
std::unique_ptr<int> intPtr = std::make_unique<int>();
int data = sensorFlag();
if(data==-1){//Error
throw std::runtime_error("Unexpected value");//Compliant
}
//...
}
}