返回对象的const引用而不是副本

Rob*_*Rob 70 c++ const

虽然重构了一些代码,但我遇到了一些返回std :: string的getter方法.像这样的东西例如:

class foo
{
private:
    std::string name_;
public:
    std::string name()
    {
        return name_;
    }
};
Run Code Online (Sandbox Code Playgroud)

当然吸气者会更好地回归const std::string&?当前方法返回的副本效率不高.会返回一个const引用会导致任何问题吗?

Dim*_*ima 55

这可能导致问题的唯一方法是调用者存储引用,而不是复制字符串,并在对象被销毁后尝试使用它.像这样:

foo *pFoo = new foo;
const std::string &myName = pFoo->getName();
delete pFoo;
cout << myName;  // error! dangling reference
Run Code Online (Sandbox Code Playgroud)

但是,由于您的现有函数返回一个副本,因此您不会破坏任何现有代码.

  • @Dima:在这种情况下,大多数编译器可能会执行命名值返回值优化. (7认同)
  • @Dale,我建议返回一个常量引用。 (2认同)

Ogr*_*m33 29

实际上,另一个特别通过引用返回字符串的问题是通过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.
Run Code Online (Sandbox Code Playgroud)

如果你的调用者要做这种事情,最好使用某种类型的智能指针或const引用,或者至少在你的foo.name()方法上有一个讨厌的警告注释标题.我提到JNI,因为以前的Java编码器可能特别容易受到这种类似的链接方法的影响,而这种方法似乎无害.

  • 该死,好抓.虽然我认为C++ 11重新定义了临时代码的生命周期,使这个代码对任何兼容的编译器都有效. (3认同)

pae*_*bal 17

const引用返回的一个问题是,如果用户编码如下:

const std::string & str = myObject.getSomeString() ;
Run Code Online (Sandbox Code Playgroud)

有了std::string回报,直到STR超出范围的临时对象将仍然活着,并连接到海峡.

但是会发生什么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.
Run Code Online (Sandbox Code Playgroud)

所以我倾向于const引用返回(因为,无论如何,我只是更喜欢发送一个引用,而不是希望编译器会优化额外的临时),只要遵守以下合同:"如果你想超越它我的对象存在,他们在我的对象被破坏之前复制它"


rle*_*lut 10

std :: string的一些实现共享具有写时复制语义的内存,因此按值返回几乎与返回引用一样有效,并且您不必担心生命周期问题(运行时确实如此)它适合你).

如果你担心性能,那么对它进行基准测试(<=不能压力那么大!!!)尝试两种方法并测量增益(或缺乏增益).如果一个人更好而你真的在乎,那就用它吧.如果没有,那么更喜欢按价值进行保护,它会再次提供其他人提到的终身问题.

你知道他们对做出假设的看法......

  • 嘿.运行时给出的东西,多线程取消了.:) (10认同)
  • @ user60628 COW已经失宠(十年前)见[本文](http://www.ddj.com/cpp/184403779) (4认同)

Fra*_*ank 7

好的,所以返回副本和返回引用之间的区别是:

  • 表现:返回参考可能会更快,也可能不会更快; 它取决于std::string您的编译器实现如何实现(正如其他人指出的那样).但即使你返回引用,函数调用后的赋值通常也会涉及一个副本,如std::string name = obj.name();

  • 安全:返回参考可能会也可能不会引起问题(悬挂参考).如果您的函数的用户不知道他们在做什么,将引用存储为引用并在提供对象超出范围后使用它,那么就会出现问题.

如果你想快速安全地使用boost :: shared_ptr.您的对象可以在内部存储字符串shared_ptr并返回shared_ptr.这样,就不会复制对象,并且它总是安全的(除非你的用户拔出原始指针get()并在你的对象超出范围之后用它做东西).

  • 注意:从C++ 11开始,有`std :: shared_ptr` (5认同)