Я знаю, что заголовок звучит знакомым, поскольку существует много подобных вопросов, но я прошу другой аспект проблемы (я знаю различие между наличием вещей на стеке и помещением их на "куче").
В 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 (new Thing ());
и только return thing;
.
shared_ptr
. Deref это как указатель. Он будет удален для вас, когда последняя ссылка на Thing
выйдет за пределы области действия. Просто создайте объект и верните его
Thing calculateThing() {
Thing thing;
// do calculations and modify thing
return thing;
}
Я думаю, вы окажете себе услугу, если забудете об оптимизации и просто напишете читаемый код (вам нужно будет запустить профилировщик позже - но не выполняйте предварительную оптимизацию).
Вы пытались использовать интеллектуальные указатели (если Вещь действительно большой и тяжелый объект), например auto_ptr:
std::auto_ptr<Thing> calculateThing()
{
std::auto_ptr<Thing> thing(new Thing);
// .. some calculations
return thing;
}
// ...
{
std::auto_ptr<Thing> thing = calculateThing();
// working with thing
// auto_ptr frees thing
}
Один из быстрых способов определить, вызывается ли конструктор копирования, - это добавить ведение журнала в конструктор копирования вашего класса:
MyClass::MyClass(const MyClass &other)
{
std::cout << "Copy constructor was called" << std::endl;
}
MyClass someFunction()
{
MyClass dummy;
return dummy;
}
Вызов someFunction
; количество строк «Копировать конструктор был вызван», которое вы получите, будет варьироваться от 0, 1 до 2. Если вы ничего не получите, значит, ваш компилятор оптимизировал возвращаемое значение (что ему разрешено). Если вы получаете не получите 0, а ваш конструктор копирования смехотворно дорог, , затем ищите альтернативные способы возврата экземпляров из ваших функций.
Просто верните такой объект:
Thing calculateThing()
{
Thing thing();
// do calculations and modify thing
return thing;
}
Это вызовет конструктор копирования для Things, так что вы можете захотеть сделать свою собственную реализацию этого. Примерно так:
Thing(const Thing& aThing) {}
Это может работать немного медленнее, но это может не быть проблемой.
Обновление
Компилятор, вероятно, оптимизирует вызов конструктора копирования, поэтому не будет дополнительных накладных расходов. (Как указано в комментарии Dreamlax).
Я уверен, что эксперт по C ++ даст лучший ответ, но лично мне нравится второй подход. Использование интеллектуальных указателей помогает решить проблему с забыванием delete
, и, как вы говорите, это выглядит чище, чем создание объекта заранее (и необходимость его удаления, если вы хотите разместить его в куче) .