Удалить выделенный (новый) объект за пределами его области видимости

#c

#c

Вопрос:

В настоящее время я сталкиваюсь с проблемой с некоторым управлением памятью в C .

 void SomeClass::cpy(char** dest, const char* origin)
{
  int len = strlen(origin);
  char* tmp = new char[len 1];
  strncpy(tmp, origin, len);
  tmp[len] = '';
  *dest = tmp;
}
 

Вызов функции выглядит следующим образом

 for(auto amp;person : persons)
...
    SomeClass::cpy(amp;(person.name_), new_name);
...
 

Моя проблема заключается в удалении переменной tmp за пределами ее области видимости. Я не могу удалить его в конце этой области, так как мне нужно его значение. Использование деструктора классов с delete[] name_; , похоже, нарушает память.

Комментарии:

1. Что вы имеете в виду вне его области видимости ?

2. Я настоятельно рекомендую вам использовать std::string и ссылаться на параметры вместо того, чтобы возиться с ручными операциями с памятью и указателями.

3. Поскольку я выделяю tmp внутри функции, я не могу удалить его из другого места.

4. Прелесть (и ужас) динамического распределения заключается в том, что масштаб — это то, о чем вы говорите.

5. @Mer0winger Уверен, что это так. Вы можете удалить его из любого места, где у вас есть доступ к указателю. Просто убедитесь, что он удален ровно один раз .

Ответ №1:

В современном C считается плохой практикой использовать открытые указатели для владения объектами. Вместо этого используйте std::unique_ptr, что гарантирует освобождение памяти при ее удалении. И предпочтительнее возвращать значение из функций вместо использования выходных аргументов. Измените сигнатуру вашего метода на

 std::unique_ptr<char> SomeClass::cpy(const char* origin)
 

Этот способ std::unique_ptr будет отвечать за удаление памяти, когда она больше не нужна.

Обратите внимание, что для вашего конкретного варианта использования это std::string может быть лучшей альтернативой, поскольку оно специально разработано для обработки строк.

Комментарии:

1. В современном C считается плохой практикой использовать открытые указатели , что немного вводит в заблуждение. Использование необработанных указателей- владельцев — плохая практика. указатели, не являющиеся владельцами, просто прекрасны.

Ответ №2:

Быстрый пошаговый:

 void SomeClass::cpy(char** dest, const char* origin)
{
  int len = strlen(origin);
  char* tmp = new char[len 1];
 

Блок памяти был просто динамически выделен. Этот блок будет существовать до тех пор, пока не будет освобожден вручную с delete[] помощью .

   strncpy(tmp, origin, len);
  tmp[len] = '';
  *dest = tmp;
 

Ранее выделенный блок памяти был назначен dest . Тот, кто предоставил dest , может использовать delete[] для освобождения этой памяти в любое время, если это выполняется только один раз за выделение.

 }
 

Теперь вызывающий

 SomeClass::cpy(amp;(person.name_), new_name);
 

указал, что person.name_ и dest являются одним и тем же. Это означает, что delete[] person.name_; это вполне приемлемо.

Однако…

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

 std::string SomeClass::cpy(const char* origin)
{
  return std::string(origin);
}
 

и

 person.name_ = SomeClass::cpy(new_name);
 

Один раз person.name_ преобразуется из char * в std::string . Но как только это будет сделано SomeClass::cpy , это будет излишним, потому что

 person.name_ = new_name;
 

Сделает всю работу за вас.

Комментарии:

1. Вы можете даже создать его и использовать std::string SomeClass::cpy(std::string origin) { return origin; }

2. @NathanOliver Хороший момент. Также открывается возможность для стяжки длиной в полторы страницы, объясняющей передачу по значению и передачу по ссылке.

Ответ №3:

Вы возвращаете tmp в *dest . Итак, когда вы закончите с этим, вы можете delete person.name_ предположить, что person.name_ это a char* .