Возврат объекта от функции

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

Сценарий A: возвращенный объект должен быть сохранен в переменной, которая не должна быть изменена в течение ее времени жизни. Таким образом,

const Foo SomeClass::GetFoo() {
 return Foo(); 
}

вызванный как:

someMethod() {
 const Foo& l_Foo = someClassPInstance->GetFoo();
//...
}

Scneraio B: возвращенный объект должен быть сохранен в переменной, которая будет изменена в течение ее времени жизни. Таким образом,

void SomeClass::GetFoo(Foo& a_Foo_ref) {
     a_Foo_ref = Foo(); 
    }

вызванный как:

someMethod() {
 Foo l_Foo;
 someClassPInstance->GetFoo(l_Foo);
//...
}

У меня есть один вопрос здесь: Позволяет говорят, что у Foo не может быть конструктора по умолчанию. Затем, как был бы Вы иметь дело с этим в этой ситуации, так как мы наклоняемся больше, пишут это:

Foo l_Foo

Сценарий C:

Foo SomeClass::GetFoo() {
 return Foo(); 
}

вызванный как:

someMethod() {
 Foo l_Foo = someClassPInstance->GetFoo();
//...
}

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

Что Вы думаете? Кроме того, Вы рекомендуете лучшему способу обработать это вместо этого?

19
задан Billy ONeal 11 April 2010 в 05:15
поделиться

2 ответа

Во-первых, давайте рассмотрим то, что здесь задействовано:

(a) Увеличение времени жизни временного объекта , когда он используется для инициализации ссылки - я узнал об этом в этой публикации ] Андрея Анександреску. Опять же, это кажется странным, но полезным:

class Foo { ... }

Foo GetFoo() { return Foo(); }  // returning temporary

void UseGetFoo()
{
   Foo const & foo = GetFoo();
   // ... rock'n'roll ...
   foo.StillHere();
}

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

(b) Оптимизация возвращаемого значения - ( wikipedia ) - две копии локальные -> возвращаемое значение -> локальные может быть опущено при определенных обстоятельствах. Это удивительное правило, поскольку оно позволяет компилятору изменять наблюдаемое поведение, но оно полезно.

Вот и все. C ++ - странно, но полезно.


Итак, глядя на ваши сценарии

Сценарий A: , вы возвращаете временное и привязываете его к ссылке - время жизни временного продлевается до времени жизни l_Foo.

Обратите внимание, что это не сработает, если GetFoo вернет ссылку, а не временную.

Сценарий B: Работает, за исключением того, что он заставляет цикл Construct-Construct-Copy-Cycle (который может быть намного дороже, чем отдельная конструкция), и проблема, о которой вы упомянули, о требовании значения по умолчанию конструктор.

Я бы не стал использовать этот шаблон для создания объекта - только для изменения существующего.

Сценарий C: Копии временных файлов могут быть опущены компилятором (согласно правилу RVO). К сожалению, нет никакой гарантии, но современные компиляторы реализуют RVO.

Ссылки Rvalue в C ++ 0x позволяют Foo реализовать конструктор похищения ресурсов, который не только гарантирует подавление копий, но также может быть полезен в других сценариях.

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


Подобный вопрос требует упоминания интеллектуальных указателей, таких как shared_ptr и unique_ptr (последний является «безопасным» auto_ptr ). Они также находятся в C ++ 0x . Они предоставляют альтернативный шаблон для функций, создающих объекты.


16
ответ дан 30 November 2019 в 04:47
поделиться

Из трех сценариев , номер 3 - идеоматический метод, и вы, вероятно, должны его использовать. Вы не будете платить за дополнительные копии, потому что компилятор может использовать copy elision , чтобы избежать копирования, если это возможно.

Secnario A ошибается.В итоге вы получаете ссылку на временный объект, который уничтожается, когда завершается выполнение оператора, содержащего вызов функции. Хорошо, сценарий A не является неправильным, но вы все равно должны использовать сценарий C.

Secnario B работает нормально, но:

Допустим, у Foo не может быть конструктора по умолчанию. Тогда как бы вы поступили с этим в данной ситуации, раз мы больше не можем писать это: Foo l_Foo .

У Foo должен быть конструктор по умолчанию. Даже если вы его не дадите, компилятор сделает это за вас. Предполагая, что вы объявляете конструктор private , вы не можете использовать этот вызывающий метод. Вам нужно либо сделать Foo конструктивным по умолчанию, либо использовать Secnario C.

4
ответ дан 30 November 2019 в 04:47
поделиться
Другие вопросы по тегам:

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