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

class foo
{
private:
    std::string name_;
public:
    std::string name()
    {
        return name_;
    }
};

当然,吸气会更好地返回 const std::string&?目前的方法是返回的一个副本而不是作为有效。会返回一个常量参考,而不是引起任何问题?

有帮助吗?

解决方案

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

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

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

其他提示

实际上,通过引用返回字符串而不是的另一个问题具体std::string通过指针提供对内部const char*的指针访问< a href =“http://www.cplusplus.com/reference/string/string/c_str/"rel =”noreferrer“> c_str()方法。这让我花了很多时间调试头痛。例如,假设我想从foo获取名称,并将其传递给JNI以用于构造jstring以便稍后传入Java,并且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返回时,临时对象将保持活动状态并附加到str,直到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.

所以我的偏好是const引用返回(因为,无论如何,我只是更喜欢发送引用而不是希望编译器优化额外的临时),只要遵守以下契约:<!> “如果你想让它超出我对象的存在,他们会在我的对象的破坏之前复制它<!> <<;

std :: string的一些实现共享内存具有写时复制语义,因此按值返回几乎与返回引用一样有效担心生命周期问题(运行时为你做了)。

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

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

好吧,所以 差异 之间的返回复制和返回的基准是:

  • 性能:返回的参照可能或不可能速度更快;这取决于如何 std::string 实现通过编译器实施(正如其他人已经指出的)。但是,即使你返回基准的后分配功能的电话,通常涉及到一个副本,作为在 std::string name = obj.name();

  • 安全:返回的参照可能或不可能引起问题(晃来晃去的参考)。如果用户的功能不知道他们在做什么,保存参考作为参考和使用后提供的对象超出范围然后有一个问题。

如果你想要它 快速和安全 使用 提升::情况.你的对象可以在内部商店的字符串 shared_ptr 和返回 shared_ptr.这样,就不会有复制的对象去,并且它始终是安全(除非你用户拉出生的指针 get() 做的东西后你的对象超出范围).

我将其更改为返回const std :: string <!> amp;。如果您不更改所有调用代码,调用者可能会复制结果,但不会引入任何问题。

如果您有多个线程调用name(),则会出现一个潜在的皱纹。如果返回引用,但稍后更改基础值,则调用者的值将更改。但是现有代码无论如何都看起来不是线程安全的。

看看Dima的答案,找出相关的潜在但不太可能的问题。

可以想象,如果来电者真的想要副本,你可能会破坏某些东西,因为他们即将改变原件,并希望保留它的副本。然而,实际上它更有可能只是返回一个const引用。

最简单的方法是尝试它然后测试它以查看它是否仍然有效,前提是您可以运行某种测试。如果没有,我会先着手编写测试,然后再继续重构。

重要吗?一旦使用现代优化编译器,按值返回的函数将不涉及副本,除非它们在语义上是必需的。

请参阅 C ++ lite常见问题解答

如果更改为const引用,则该函数的典型用法不会中断。

如果调用该函数的所有代码都在您的控制之下,只需进行更改并查看编译器是否抱怨。

取决于您需要做什么。也许您希望所有调用者在不更改类的情况下更改返回值。如果返回不会飞的const引用。

当然,下一个论点是调用者可以自己制作副本。但是如果您知道该函数将如何使用并且知道无论如何都会发生这种情况,那么这样做可能会让您在代码中稍后一步。

我通常会返回const <!>放大器;除非我不能。 QBziZ举例说明了这种情况。当然QBziZ还声称std :: string具有写时复制语义,这在今天很少如此,因为COW在多线程环境中涉及很多开销。通过返回const <!> amp;你把责任放在调用者身上,用他们最后的字符串做正确的事。但是,由于您正在处理已在使用的代码,因此除非分析显示复制此字符串导致大量性能问题,否则您可能不应更改它。然后,如果你决定改变它,你将需要彻底测试,以确保你没有破坏任何东西。希望与你合作的其他开发人员不要像Dima的回答那样做粗略的事情。

返回对成员的引用会公开该类的实现。 这可以防止改变班级。对于私有或受保护的方法可能有用,因为需要进行优化。 C ++ getter应返回什么内容

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top