Я знаю, что название звучит знакомо, так как есть много похожих вопросов, но я спрашиваю о другом аспекте проблемы (я знаю разницу между тем, чтобы что-то складывалось в стек и помещалось в кучу).
В Java я всегда могу вернуть ссылки на "локальные" объекты
public Thing calculateThing() {
Thing thing = new Thing();
// do calculations and modify thing
return thing;
}
В C ++, чтобы сделать что-то подобное, у меня есть 2 варианта
(1) Я могу использовать ссылки всякий раз, когда мне нужно «вернуть» объект
void calculateThing(Thing& thing) {
// do calculations and modify thing
}
Тогда используйте это как это
Thing thing;
calculateThing(thing);
(2) Или я могу вернуть указатель на динамически размещенный объект
Thing* calculateThing() {
Thing* thing(new Thing());
// do calculations and modify thing
return thing;
}
Тогда используйте это как это
Thing* thing = calculateThing();
delete thing;
При первом подходе мне не придется освобождать память вручную, но для меня это затрудняет чтение кода. Проблема со вторым подходом заключается в том, что мне придется помнить delete thing;
, что выглядит не очень хорошо. Я не хочу возвращать скопированное значение, потому что оно неэффективно (я думаю), поэтому здесь возникают вопросы
- Есть ли третье решение (которое не требует копирования значения)?
- Есть ли проблема, если я придерживаюсь первого решения?
- Когда и почему я должен использовать второе решение?
источник
Ответы:
Докажите это.
Посмотрите RVO и NRVO, и в C ++ 0x семантика перемещения. В большинстве случаев в C ++ 03 параметр out - это всего лишь хороший способ сделать ваш код некрасивым, а в C ++ 0x вы действительно навредите себе, используя параметр out.
Просто напишите чистый код, верните по значению. Если производительность является проблемой, профилируйте ее (перестаньте догадываться) и найдите, что вы можете сделать, чтобы это исправить. Скорее всего, не будет возвращать вещи из функций.
Тем не менее, если вы намерены писать так, вы, вероятно, захотите сделать параметр out. Это позволяет избежать динамического выделения памяти, что безопаснее и, как правило, быстрее. Это требует, чтобы у вас был какой-то способ создать объект перед вызовом функции, что не всегда имеет смысл для всех объектов.
Если вы хотите использовать динамическое размещение, самое малое, что можно сделать, это поместить его в умный указатель. (В любом случае это следует делать постоянно). Тогда вам не нужно ничего удалять, вещи безопасны для исключений и т. Д. Единственная проблема - это, вероятно, медленнее, чем возврат по значению в любом случае!
источник
Просто создайте объект и верните его
Я думаю, вы сделаете себе одолжение, если забудете об оптимизации и просто напишите читаемый код (вам нужно будет запустить профилировщик позже - но не предварительно оптимизируйте).
источник
Thing thing();
объявляет локальную функцию и возвращаетThing
.Просто верните объект так:
Это вызовет конструктор копирования в Things, так что вы можете захотеть сделать это самостоятельно. Как это:
Это может работать немного медленнее, но это не может быть проблемой вообще.
Обновить
Компилятор, вероятно, оптимизирует вызов конструктора копирования, поэтому никаких дополнительных затрат не будет. (Как и DreamLax указано в комментарии).
источник
Thing thing();
объявляет локальную функцию, возвращающуюThing
, также, стандарт позволяет компилятору опускать конструктор копирования в представленном вами случае; любой современный компилятор, вероятно, сделает это.Вы пытались использовать умные указатели (если Thing действительно большой и тяжелый объект), например, auto_ptr:
источник
auto_ptr
s устарели; использоватьshared_ptr
илиunique_ptr
вместоОдин быстрый способ определить, вызывается ли конструктор копирования, - добавить запись в конструктор копирования вашего класса:
Звонок
someFunction
; количество строк «Копировать конструктор был вызван», которые вы получите, будет варьироваться между 0, 1 и 2. Если вы не получили ни одной, ваш компилятор оптимизировал возвращаемое значение (что ему разрешено делать). Если вы не получаете 0, и ваш конструктор копирования смехотворно дорого, то искать альтернативные пути , чтобы вернуть экземпляры из ваших функций.источник
Во-первых, у вас есть ошибка в коде, вы имеете в виду
Thing *thing(new Thing());
, и толькоreturn thing;
.shared_ptr<Thing>
. Разыщите его, как указатель. Он будет удален для вас при последнем обращении кThing
содержимое выйдет из области видимости.источник
Я уверен, что эксперт C ++ придет с лучшим ответом, но лично мне нравится второй подход. Использование умных указателей помогает решить проблему забвения
delete
и, как вы говорите, выглядит чище, чем необходимость создания объекта перед рукой (и все же необходимости его удаления, если вы хотите разместить его в куче).источник