Кто несет ответственность за освобождение выделенной памяти? - PullRequest
0 голосов
/ 14 марта 2012

Рассмотрим следующий фрагмент кода

void xyz(CString **mapping)
{
    *mappings = new CString[10];
    (*mappings)[0] = "hello";
    //...
}

void main(int argc, char **argv)
{
    CString *tmp;
    xyz(&tmp);
    // now we have the CString array defined in xyz
}

Я пытаюсь заполнить переменную из main некоторыми значениями, сгенерированными другой функцией.Я прочитал, что лучше всего удалить / освободить в той же функции, в которой он был выделен.Это невозможно в этом случае, потому что единственная причина, по которой существует xyz, заключается в генерации данных (это всего лишь пример, в реальных случаях в xyz ;) может возникнуть некоторая сложность).Я также подумал о создании массива в стеке в main и передаче его функции, но в моем случае размер массива в то время не фиксирован (он определяется в xyz).Какой самый чистый и самый распространенный способ очистки выделенной памяти?Если бы у нас был объект с методом xyz, что тогда было бы лучше?Чтобы создать другой метод (например, freeMapping()), который должен вызывать вызывающий объект после обработки данных?

Ответы [ 3 ]

1 голос
/ 14 марта 2012

Есть много разных схем, которые люди использовали на протяжении многих лет, некоторые лучше или хуже. Хорошие стратегии определяют жесткую и последовательную модель, которая устанавливает правила «владения» ресурсом, то есть ответственность за очистку и обеспечение того, чтобы ничто не осуществляло незаконный доступ к ресурсу. Правила успешных стратегий также таковы, что для безопасного доступа к ресурсу необходимо только локальное представление о ресурсе и способ его использования.

Стратегия, которую вы должны использовать в современном C ++, называется RAII или «получение ресурсов - инициализация». Это имя означает, что любой приобретенный ресурс должен быть инициализацией. Например:

std::string s = "Hello, World";

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

Использование RAII не только удобно для обеспечения нормального управления локализованным ресурсом, но также значительно упрощает правильную очистку ресурсов при наличии исключений. При выходе из области действия из-за исключения C ++ гарантирует уничтожение всех полностью созданных объектов в этой области. Если необходимые задачи для очистки ресурсов выполняются деструкторами объектов, то ресурсы не будут вытекать, и нет необходимости добавлять явную обработку исключений для каждой области, в которой используется ресурс.

C ++ уже включает классы, владеющие ресурсами для многих типов ресурсов. Для массива динамического размера используйте std::vector:

std::vector<CString> xyz()
{
    // C++11
    return {"hello",...};

    // or C++03
    std::vector<CString> mappings;
    mappings.push_back("hello");
    ...
    return mappings
}

void main(int argc, char **argv)
{
    std::vector<CString> tmp = xyz();
    // now we have the CString array defined in xyz
    // the array gets automatically cleaned up by std::vector's destructor
}
0 голосов
/ 14 марта 2012

Класс стиля RAII (обертка, которая освобождает объект в его dtor) является распространенным способом обработки такого рода вещей (см. Std :: auto_ptr или некоторые из более современных альтернатив, таких как boost :: scoped_ptr или std: : unique_ptr). Когда auto_ptr / unique_ptr выходит из области видимости, автоматически вызывается delete.

Передача ссылки на std :: vector, созданный в стеке вызывающих, может работать.

Возвращение std :: vector по значению легко понять и может работать довольно хорошо в зависимости от ваших требований.

0 голосов
/ 14 марта 2012

Это плохая практика, которую вы показываете здесь.Таким образом, у него есть 2 решения:

  1. Выделить и освободить память в основной функции.

  2. Создать класс, который будет отвечать за все операции сэту строку и использовать ее в основной функции.

...