Принцип "выделение ресурса есть инициализация"


На главную

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

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

void f() {
FILE* pf;
if ((pf = fopen("myfile.txt", "rt")) == NULL) {
//не удалось открыть файл
exit(1);
}

//здесь выполняем нужную работу с файлом
//и здесь может возникнуть какое-нибудь исключение
//...


//если исключение возникнет, то до этой строки мы не доберемся
//и файл не будет закрыт
fclose(pf);
}

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

class FileOpen {
FILE* pf;
public:
FileOpen(char* filename, char* mode) {
if ((pf = fopen(filename, mode)) == NULL) {
//вывести сообщение о том, что файл открыть
//не удалось
}
}
~FileOpen() { fclose(pf); }
//...
};

void f() {
FileOpen("myfile.txt", "rt");

//здесь выполняем нужную работу с файлом
//...

}

Скачать реализацию примера с открытием файла


Для поддержания принципа "выделение ресурса есть инициализация" Вы можете также использовать класс auto_ptr (сокращение от automatic pointer - автоматический указатель), который предоставляется стандартной библиотекой С++.
Класс auto_ptr предназначен для работы с объектами, которые необходимо удалять явно (как правило это объекты, созданные динамически с помощью оператора new).
При создании объекта класса auto_ptr параметром конструктора должен быть указатель на динамический объект. В дальнейшем c auto_ptr можно работать почти как с обычным указателем, который указывает на тот же динамический объект, на который указывал исходный указатель. При этом программисту не нужно заботиться о явном удалении динамического объекта, т.к. он в любом случае будет автоматически удален деструктором auto_ptr.

Определение класса auto_ptr в стандартной библиотеке в сокращенном виде:

template<class X>
class Std::auto_ptr {
//...
X* ptr;
public:
//конструктор и деструктор
explicit auto_ptr (X* p = 0) throw() { ptr = p;}
~auto_ptr() throw() {delete ptr;}

//оператор разыменования позволяте получить объект
X& operator*() const throw() {return *ptr;}

//оператор -> позволяет получить указатель
X* operator->() const throw() {return ptr;}

//...
};
Использование auto_ptr позволяет устранить многие ошибки, допускаемые при работе с указателями, и в первую очередь ошибки, приводящие к утечке памяти.

Источники:
Страуструп Б. "Язык программирования С++", 2001.
Саттер Г. "Решение сложных задач на С++", 2002.

На главную

Rambler's Top100
Хостинг от uCoz