Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Для новичков > +Выделение и освобождение памяти на разных уровнях


Автор: zim22 27.6.2009, 14:16
Выполняю задание из Страуструпа.
Цитата

Напишите функцию cat, принимающую в качестве аргументов две C-строки, которая возвращает строку, являющуюся их конкатенацией. Воспользуйтесь оператором new для выделения памяти под результат.


Функцию я написал:
Код

const char *cat(const char *lhs, const char *rhs) {
  char *ret = new char[strlen(lhs) + strlen(rhs) + 1];
  strcpy(ret, lhs);
  strcat(ret, rhs);
  return ret;
}

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

const char *str = cat("first_", "second");
delete [] str;

как тогда переписать функцию, чтобы удовлетворялось требование "уровня"? есть ли такое требование вообще? smile

Автор: zkv 27.6.2009, 14:32
zim22, безусловно, так писать нельзя. А если копнуть глубже, то new вообще пользоваться не стоит. 

Полагаю, учебная задача? Так сделайте то, что вас просят, для себя главное отложите, что это неправильно.
Можно подготовить два варианта: правильный и соответствующий заданию, при сдаче пояснить. 
Наверное так бы и поступил.

PS Радует то, что такой вопрос родился, многие бы просто прошли мимо, сделав то, что их просят.

Автор: zim22 27.6.2009, 14:38
Цитата(zkv @  27.6.2009,  14:32 Найти цитируемый пост)
Полагаю, учебная задача? 

нет. задача не учебная. мне "для себя" необходимо понять, в чём ошибка "проектирования" метода и как её избежать.

Автор: Andrew121 27.6.2009, 14:54
Я в таких случаях предварительно выделяю память, и передаю указатель.

Автор: zkv 27.6.2009, 15:00
zim22, вы сами ответили на свой вопрос, есть необходимость использования new - используйте в там, где можно использовать и delete.
В данном случае, память следует выделять за пределами функции cat, там же ее (память) и освобождать.
Ну а если С++, то юзать чего нить типа boost::shared_ptr (который вроде ныне известен как std::tr1::shared_ptr).
Хотя если совсем C++, то тут и подобной задачи не возникнет: есть сущность - реализуй ее классом.

Автор: zim22 27.6.2009, 15:13
Цитата(Andrew121 @  27.6.2009,  14:54 Найти цитируемый пост)
Я в таких случаях предварительно выделяю память, и передаю указатель.

думаю, пока остановлюсь на этом же варианте.
Код

void cat(char *buffer, const char *lhs, const char *rhs) {  
  strcpy(buffer, lhs);
  strcat(buffer, rhs);
}

char *s1 = "first_";
char *s2 = "second";

char *buf = new char[strlen(s1) + strlen(s2) + 1];
cat(buf, s1, s2);
delete [] buf;

Автор: maxim1000 27.6.2009, 17:23
есть очень простой принцип, которому стоит следовать в большинстве (если не во всех) случаев: ответ на вопрос о владении объектом должен быть очевиден, всё остальное - способы достижения этой очевидности
(под владением подразумевается, кто должен удалить этот объект)
а способы такие:
1. стековые объекты
2. boost::shared_ptr, boost::scoped_ptr, std::auto_ptr
3. близость создания и удаления в коде
4. документация (имена функций, переменных, комментарии)
(попробовал упорядочить по "степени надёжности", но это может быть субъективно)

в некоторых случаях последний способ не так уж и страшен
например, паттерн "фабрика" часто как раз и предполагает наличие метода, который создаёт объект, но удалением занимается кто-то другой (возможно, даже не метод этой фабрики)

использовать умные указатели на этом уровне не всегда хорошо, т.к. каждый тип умного указателя навязывает свою стратегию владения, а иногда это решение стоит отдать пользователю кода, который создаёт объекты

так что в таких случаях можно, например, назвать функцию CreateConcatenatedString, по названию вполне понятно, что строка была создана, а значит, вызывающий код ответственен за её удаление

чтобы выделить память заранее, пришлось бы вынести часть логики, специфичной для функции сложения (вычисление длины) наружу, а это тоже не очень хорошо

в большинстве же случаев, конечно new и delete стоит оставить в специальных классах, которые контролируют время жизни объектов (в данном случае им вполне мог бы быть std::string, или что-то более общее типа boost::shared_array)

Автор: dumb 28.6.2009, 03:46
Цитата(maxim1000 @  27.6.2009,  18:23 Найти цитируемый пост)
в таких случаях можно, например, назвать функцию CreateConcatenatedString
и, как правило, к таким функциям добавляются DestroyConcatenatedString или DestroyString(если есть группа создающих функций), даже если их содержание = delete, так как вовсе не очевиден метод выделения памяти внутри создающей функции.

Автор: maxim1000 28.6.2009, 13:47
Цитата(dumb @  28.6.2009,  03:46 Найти цитируемый пост)
и, как правило, к таким функциям добавляются DestroyConcatenatedString или DestroyString(если есть группа создающих функций), даже если их содержание = delete, так как вовсе не очевиден метод выделения памяти внутри создающей функции. 

и то правда
впрочем, не так уж и редко случается, что delete - единственный, используемый в программе, способ для удаления указателей

хотя обёртка типа std::string естественно лучше

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)