Кто отвечает за освобождение выделенной памяти?

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

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
}

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


person pluckyDuck    schedule 13.03.2012    source источник
comment
С или С++, решать. Потому что в C++ вы просто используете std::vector.   -  person Cat Plus Plus    schedule 14.03.2012
comment
Если вы говорите о C++, то оберните выделенные данные в объект, который освобождает память, когда он выходит за рамки.   -  person Tony    schedule 14.03.2012
comment
Ваше последнее редактирование изменило вопрос о С++ на вопрос о C, что сделало бы ответы недействительными. Если у вас есть новый вопрос, выполните поиск на сайте, и если это не поможет (не будет), задайте новый вопрос.   -  person Mooing Duck    schedule 11.05.2015


Ответы (3)


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

Стратегия, которую вы должны использовать в современном 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
}
person bames53    schedule 14.03.2012

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

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

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

person yozhik    schedule 13.03.2012

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

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

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

person evarsanyi    schedule 13.03.2012
comment
RAII — это нечто большее, чем просто обертки вокруг ресурсов, которые освобождают ресурс в dtor. Также интеллектуальные указатели не отделены от RAII; они реализуют RAII для указателей с различной семантикой владения. - person bames53; 14.03.2012