Отложенное выделение ресурсов

Последнее обновление: 04.09.2023

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

void someFunction()
{
    if((resource1=allocateResource1()) && (resource2=allocateResource2()))
    {
        // основной код
    }
    if(resource1) // если выделен resource1
    {
        freeResource1(); // очищаем его
    }
    if(resource2) // если выделен resource2
    {
        freeResource2(); // очищаем его
    }
}

И в данном случае, когда начинает выполняться первая конструкция if, то сначала выполняется выражение resource1=allocateResource1(), которое условно выделяет память для ресурса resource1. Если это выражение выполнилось успешно и возвратило ненулевое значение, выполняется второй операнд операции && - выражение resource2=allocateResource2(), которое условно выделяет второй ресурс. И если второй ресурс также успешно выделен, то выполняются действия этой конструкции if.

После завершения первой конструкции if выполняется освобождение ресурсов, если они были успешно выделены. Таким образом, мы уходим от вложенности ifов. А освобождение ресурсов целенаправлено выполняется в одном месте - в конце функции. В то же время если надо выделить много ресурсов, то это перегружает условие в конструкции if, что опять же может усложнить ее понимание, отладку.

Применим этот паттерн на примере программы, которая ищет определенное слово в некотором файле:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

// результат поиска
typedef enum {ERROR, FOUND, NOTFOUND} status;

status find(char*, char*, size_t);
status find_word(FILE*, char*, size_t, char*);

status find(char* file_name, char* word, size_t word_size)
{
    FILE* file_pointer = 0;
    char* buffer = 0;
    status result = ERROR;

    // если не соблюдаются предусловия, завершаем функцию
    assert(file_name!=NULL && "File name is invalid!");
    assert(word!=NULL && "Word is invalid!");

   // если удалось открыть файл и выделить память
    if((file_pointer=fopen(file_name, "r")) && (buffer=malloc(word_size))) 
    {
        result = find_word(file_pointer, word, word_size, buffer); // собственно поиск слова
    }
        
    // секция освобождения ресурсов
    if(buffer) free(buffer);
    if(file_pointer) fclose(file_pointer);

    return result;
}
// поиск слова
status find_word(FILE* file_pointer, char* word, size_t word_size, char* buffer){

    while(fgets(buffer, word_size, file_pointer)){
        
        if(strcmp(word, buffer)==0){ 

            return FOUND;   // если нашли слово
        }
    }
    return NOTFOUND;        // если не нашли слово
}

int main(void){
    char* filename ="test.txt";
    char word[] = "hello";
    size_t size = sizeof(word);

    status result = find(filename, word, size);

    switch(result) {
        case ERROR:
            printf("Error!\n");
            break;
        case NOTFOUND:
            printf("Text not found...\n");
            break;
        case FOUND:
            printf("Success! Text found!\n");
            break;
        
    }
}

Здесь функция find() получает извне имя файла для поиска, слово для поиска и размер слова. Чтобы найти слово, открываем файл и затем выделяем буфер в памяти для считывания данных. И только если эти операции выполнились успешно, то с помощью функции find_word() считываем символы из файла в буфер и проверяем на их соответствие искомому слову:

if((file_pointer=fopen(file_name, "r")) && (buffer=malloc(word_size))) 
{
    result = find_word(file_pointer, word, word_size, buffer); // собственно поиск слова
}

В конце функции find() проверяем выделенные ресурсы и очищаем их

// секция освобождения ресурсов
if(buffer) free(buffer);
if(file_pointer) fclose(file_pointer);
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850