Как “возвратить объект” в C++?

Я знаю, что заголовок звучит знакомым, поскольку существует много подобных вопросов, но я прошу другой аспект проблемы (я знаю различие между наличием вещей на стеке и помещением их на "куче").

В 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;, который не выглядит довольно хорошим. Я не хочу возвращать скопированное значение, потому что это неэффективно (я думаю), таким образом, здесь прибывший вопросы

  • Существует ли третье решение (который не требует копирования значения)?
  • Есть ли какая-либо проблема, если я придерживаюсь первого решения?
  • Когда и почему я должен использовать второе решение?
155
задан Shoe 30 April 2014 в 09:39
поделиться

7 ответов

Я не хочу возвращать скопированное значение, потому что это неэффективно

Докажите.

Найдите RVO и NRVO, а в C ++ - семантику перемещения 0x. В большинстве случаев в C ++ 03 параметр out - это просто хороший способ сделать ваш код некрасивым, а в C ++ 0x вы действительно навредите себе, используя параметр out.

Просто напишите чистый код, возврат по значению. Если производительность является проблемой, профилируйте ее (перестаньте гадать) и найдите, что вы можете сделать, чтобы ее исправить. Скорее всего, он не будет возвращать вещи из функций.


Тем не менее, если вы твердо настроены писать подобное, вы, вероятно, захотите сделать параметр out. Он избегает динамического выделения памяти, что безопаснее и, как правило, быстрее. Это действительно требует, чтобы у вас был какой-то способ сконструировать объект до вызова функции, что не всегда имеет смысл для всех объектов.

Если вы хотите использовать динамическое размещение, самое меньшее, что можно сделать, - это поместить его в интеллектуальный указатель. (В любом случае это следует делать постоянно). Тогда вы не будете беспокоиться об удалении чего-либо, все будет безопасным для исключений и т. Д. Единственная проблема в том, что в любом случае это, скорее всего, медленнее, чем возврат по значению!

104
ответ дан 23 November 2019 в 21:58
поделиться

Во-первых, у вас есть ошибка в коде, вы хотите иметь Thing * thing (new Thing ()); и только return thing; .

  • Используйте shared_ptr . Deref это как указатель. Он будет удален для вас, когда последняя ссылка на Thing выйдет за пределы области действия.
  • Первое решение очень часто встречается в наивных библиотеках. У него есть некоторая производительность и синтаксические накладные расходы, по возможности избегайте этого
  • . Используйте второе решение только в том случае, если вы можете гарантировать, что исключения не будут сгенерированы, или когда производительность абсолютно критична (вы будете взаимодействовать с C или сборкой, прежде чем это даже станет соответствующие).
1
ответ дан 23 November 2019 в 21:58
поделиться

Просто создайте объект и верните его

Thing calculateThing() {
    Thing thing;
    // do calculations and modify thing
     return thing;
}

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

41
ответ дан 23 November 2019 в 21:58
поделиться

Вы пытались использовать интеллектуальные указатели (если Вещь действительно большой и тяжелый объект), например 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 
}
11
ответ дан 23 November 2019 в 21:58
поделиться

Один из быстрых способов определить, вызывается ли конструктор копирования, - это добавить ведение журнала в конструктор копирования вашего класса:

MyClass::MyClass(const MyClass &other)
{
    std::cout << "Copy constructor was called" << std::endl;
}

MyClass someFunction()
{
    MyClass dummy;
    return dummy;
}

Вызов someFunction ; количество строк «Копировать конструктор был вызван», которое вы получите, будет варьироваться от 0, 1 до 2. Если вы ничего не получите, значит, ваш компилятор оптимизировал возвращаемое значение (что ему разрешено). Если вы получаете не получите 0, а ваш конструктор копирования смехотворно дорог, , затем ищите альтернативные способы возврата экземпляров из ваших функций.

8
ответ дан 23 November 2019 в 21:58
поделиться

Просто верните такой объект:

Thing calculateThing() 
{
   Thing thing();
   // do calculations and modify thing
   return thing;
}

Это вызовет конструктор копирования для Things, так что вы можете захотеть сделать свою собственную реализацию этого. Примерно так:

Thing(const Thing& aThing) {}

Это может работать немного медленнее, но это может не быть проблемой.

Обновление

Компилятор, вероятно, оптимизирует вызов конструктора копирования, поэтому не будет дополнительных накладных расходов. (Как указано в комментарии Dreamlax).

16
ответ дан 23 November 2019 в 21:58
поделиться

Я уверен, что эксперт по C ++ даст лучший ответ, но лично мне нравится второй подход. Использование интеллектуальных указателей помогает решить проблему с забыванием delete , и, как вы говорите, это выглядит чище, чем создание объекта заранее (и необходимость его удаления, если вы хотите разместить его в куче) .

0
ответ дан 23 November 2019 в 21:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: