例如:
这些中哪个是最好的:
std::string f() {}
或者const std::string& f() {}
例如:
这些中哪个是最好的:
std::string f() {}
或者const std::string& f() {}
函数不应该返回局部对象/变量的引用,因为这些对象在函数返回时会超出其作用域并被销毁。
但是,函数可以返回一个指向作用域不受函数上下文限制的对象的const或非const引用。典型的例子是自定义的operator<<
:
std::ostream & operator<<(std::ostream &out, const object &obj)
{
out << obj.data();
return out;
}
不幸的是,按值返回具有其性能缺陷。正如Chris所提到的,按值返回对象涉及复制临时对象及其后续销毁。复制通过拷贝构造函数或operator=
进行。为避免这些低效,智能编译器可以应用RVO或NRVO优化,但在某些情况下,它们无法处理--多个返回。
即将推出的C++0x标准(部分可在gnu gcc-4.3中使用)引入了右值引用[&&],可用于区分左值和右值引用。通过这种方式,可以实现移动构造函数,有助于返回对象并部分避免复制构造函数和临时对象的析构成本。
移动构造函数基本上是Andrei几年前在文章http://www.ddj.com/database/184403855中设想的内容,由Chris提出建议。
移动构造函数具有以下签名:
// move constructor
object(object && obj)
{}
它应该接管传递对象的内部所有权,使后者处于默认状态。通过这样做,避免了内部副本并简化了临时对象的销毁。典型的函数工厂将采用以下形式:
object factory()
{
object obj;
return std::move(obj);
}
std::move()
返回一个对象的右值引用。最后但并非最不重要的,移动构造函数允许将不可复制的对象以右值引用的方式返回。
我想补充一下尼古拉出色的回答。是的,你决不能返回悬空引用(例如指向局部变量的引用),但在这种情况下有三种有用的方法可以提高性能:
返回值优化(RVO):您通过值返回,但通过仅使用一个 return
语句创建返回值来消除复制,从而避免复制。以下是使用RVO的示例:How can I tokenize a C++ string?
命名返回值优化(NRVO):您通过值返回,并首先在函数顶部声明返回值变量。所有return
语句都返回该变量。对于支持NRVO的编译器,该变量分配在返回值插槽中,并且在返回时不会被复制。例如:
string
foobar()
{
string result;
// fill in "result"
return result;
}
使用shared_ptr
或类似的内容作为返回类型;这需要在堆上创建您的对象,而不是栈。这可以防止悬空引用问题,同时仍然不需要复制整个对象,只需要智能指针。
顺便说一下,我不能为RVO和NRVO的信息负责;它们直接来自Scott Meyers 的More Effective C++。由于我目前没有书在身边,因此我的描述中有任何错误都是我的问题,而不是Scott的问题。 :-)
如果你返回对函数内局部变量的引用,那么你可能会遇到问题(取决于编译器及其设置)。
如果你返回对在函数返回时仍在作用域范围内的实例的引用,则它将更快,因为不需要创建字符串的副本。
因此,后者在技术上更高效,但根据返回引用的内容,可能无法按预期工作。