В С++ 11 мы в некоторых случаях ориентируемся на передачу объектов по значению, а в других - на const-reference. Однако это руководство зависит от реализации метода, а не только от его интерфейса и предполагаемого использования его клиентами.
Когда я пишу интерфейс, я не знаю, как он будет реализован. Есть ли хорошее правило для написания сигнатур методов? Например, в следующем фрагменте кода следует использовать Bar1
или Bar2
?
class IFoo
{
public:
virtual void Bar1(std::string s) = 0;
virtual void Bar2(const std::string& s) = 0;
};
Вы можете перестать читать здесь, если согласитесь, что правильная подпись зависит от реализации. Вот пример, который показывает, почему я так считаю.
В следующем примере мы должны передать строку по значению:
class Foo
{
std::string bar;
Foo(std::string byValue)
: bar(std::move(byValue))
{
}
};
Теперь мы можем эффективно создавать Foo во всех случаях:
Foo foo1("Hello world"); // create once, move once
Foo foo2(s); // the programmer wants to copy s. One copy and one move
Foo foo3(std::move(t)); // the programmer does not need t anymore. No copy at all
В других случаях мы предпочитаем передавать объекты по ссылке const. Например, в следующем случае мы никогда не хотим копировать/хранить аргумент, просто используйте его методы:
void DoStuff(const std::string& byRef)
{
std::cout << byRef.length() << std::endl;
}
Все возможные способы использования вышеуказанного метода уже настолько эффективны, насколько это возможно.
Обновление
Я считаю, что забыл показать проблемы с альтернативой const-reference. Если вышеуказанный класс Foo
был реализован следующим образом:
class Foo
{
std::string bar;
Foo(const std::string& byRef)
: bar(byRef)
{
}
};
Тогда мы получили бы следующие результаты:
Foo foo1("Hello world"); // Here we would have one more copy of the string. It is less efficient.
Foo foo2(s); // One copy, like before
Foo foo3(std::move(t)); // Irrelevant here.
Алекс.