Я действительно смущен теперь на как и который метод использовать для эхо-сигнала от функции. Я хочу некоторую обратную связь на решениях для данных требований.
Сценарий 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();
//...
}
Я думаю, что это не рекомендуемый подход, так как он подвергся бы создающим дополнительным временным файлам.
Что Вы думаете? Кроме того, Вы рекомендуете лучшему способу обработать это вместо этого?
Во-первых, давайте рассмотрим то, что здесь задействовано:
(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 . Они предоставляют альтернативный шаблон для функций, создающих объекты.
Из трех сценариев , номер 3 - идеоматический метод, и вы, вероятно, должны его использовать. Вы не будете платить за дополнительные копии, потому что компилятор может использовать copy elision , чтобы избежать копирования, если это возможно.
Secnario A ошибается.В итоге вы получаете ссылку на временный объект, который уничтожается, когда завершается выполнение оператора, содержащего вызов функции. Хорошо, сценарий A не является неправильным, но вы все равно должны использовать сценарий C.
Secnario B работает нормально, но:
Допустим, у Foo не может быть конструктора по умолчанию. Тогда как бы вы поступили с этим в данной ситуации, раз мы больше не можем писать это:
Foo l_Foo
.
У Foo должен быть конструктор по умолчанию. Даже если вы его не дадите, компилятор сделает это за вас. Предполагая, что вы объявляете конструктор private
, вы не можете использовать этот вызывающий метод. Вам нужно либо сделать Foo
конструктивным по умолчанию, либо использовать Secnario C.