在重构某些代码时,我遇到了一些返回std :: string的getter方法。例如:
class foo
{
private:
std::string name_;
public:
std::string name()
{
return name_;
}
};
当然,吸气剂返回一个更好const std::string&
?当前方法返回的副本效率不高。返回const引用会引起任何问题吗?
在重构某些代码时,我遇到了一些返回std :: string的getter方法。例如:
class foo
{
private:
std::string name_;
public:
std::string name()
{
return name_;
}
};
当然,吸气剂返回一个更好const std::string&
?当前方法返回的副本效率不高。返回const引用会引起任何问题吗?
Answers:
可能导致问题的唯一方法是,调用方是否存储引用,而不是复制字符串,并在对象被销毁后尝试使用它。像这样:
foo *pFoo = new foo;
const std::string &myName = pFoo->getName();
delete pFoo;
cout << myName; // error! dangling reference
但是,由于您现有的函数返回一个副本,因此您不会破坏任何现有代码。
编辑:现代C ++(即C ++ 11及更高版本)支持返回值优化,因此不再按值返回事物。仍然应该记住按值返回非常大的对象,但是在大多数情况下应该可以。
实际上,另一个专门针对不通过引用返回字符串的问题是,通过c_str()方法std::string
提供了通过指向内部的指针的访问的事实。这使我花了很多小时来调试。例如,假设我想从foo中获取名称,并将其传递给JNI,以用于构造一个jstring以便稍后传递给Java,这将返回一个副本而不是一个引用。我可能会这样写:const char*
name()
foo myFoo = getFoo(); // Get the foo from somewhere.
const char* fooCName = foo.name().c_str(); // Woops! foo.name() creates a temporary that's destructed as soon as this line executes!
jniEnv->NewStringUTF(fooCName); // No good, fooCName was released when the temporary was deleted.
如果您的调用者将要执行此类操作,则最好使用某种类型的智能指针或const引用,或者至少在foo.name()方法上使用讨厌的警告注释标头。我之所以提到JNI,是因为以前的Java编码人员可能特别容易受到这种类型的方法链接的攻击,而这些方法看起来似乎无害。
const引用返回的一个问题是用户是否编码如下:
const std::string & str = myObject.getSomeString() ;
有了std::string
回报,直到海峡进入的范围之临时对象将仍然活着,并连接到海峡。
但是使用a会发生什么const std::string &
?我的猜测是,我们将有一个const引用到一个对象,该对象在其父对象解除分配时可能会死亡:
MyObject * myObject = new MyObject("My String") ;
const std::string & str = myObject->getSomeString() ;
delete myObject ;
// Use str... which references a destroyed object.
因此,我偏爱const引用返回(因为无论如何,我只希望发送一个引用而不是希望编译器优化额外的临时文件),只要遵守以下约定:我的物体的存在,他们会在我的物体销毁之前复制它”
std :: string的某些实现使用写时复制语义共享内存,因此按值返回几乎可以与按引用返回一样高效,并且您不必担心生命周期问题(运行时确实给你)。
如果您担心性能,请对其进行基准测试(<=不能足够强调)!!!尝试两种方法并测量增益(或增益不足)。如果一个更好,并且您真的在乎,请使用它。如果不是这样,则更喜欢按值来提供保护,从而提供其他人提到的终生问题。
你知道他们说些什么...
好的,因此返回副本和返回引用之间的区别是:
性能:返回参考可能更快,也可能不会更快;它取决于std::string
编译器实现的实现方式(正如其他人指出的那样)。但是,即使您返回引用,函数调用后的赋值通常也包含一个副本,例如std::string name = obj.name();
安全:返回参考可能会或可能不会导致问题(悬挂参考)。如果函数的用户不知道自己在做什么,将引用存储为引用,并在提供对象超出范围后使用它,那么就存在问题。
如果您想要快速安全地使用boost :: shared_ptr。您的对象可以在内部将字符串存储为,shared_ptr
并返回shared_ptr
。这样,就不会复制要复制的对象,而且它始终是安全的(除非您的用户get()
在对象超出范围后拉出原始指针并对其进行处理)。
有关系吗?使用现代优化的编译器后,按值返回的函数将不包含副本,除非语义上需要它们。
有关此信息,请参见C ++ lite常见问题解答。
返回对成员的引用将公开该类的实现。这样可以防止更改类。如果需要优化,可能对私有方法或受保护方法有用。 C ++ getter应该返回什么