在一个getter函数中返回一个const引用或一个副本?
默认情况下 ,从getter函数返回副本(1)或引用(2)会更好吗?
class foo { public: std::string str () { // (1) return str_; } const std::string& str () { // (2) return str_; } private: std::string str_; };
我知道2)可能会更快,但不必由于(N)RVO。 1)对于悬挂引用是比较安全的,但是对象可能会死亡或者引用永远不会被存储。
当你写一个class级的时候,你的默认设置是什么,而且还不知道性能和生命期问题是否重要?
额外的问题:当成员不是一个普通的string,而是一个vector时,游戏是否会改变?
那么这真的取决于你期望的行为 ,默认情况下。
你希望调用者看到他们对str_ unbeknownst(一个字!)所做的改变吗? 那么你需要传回一个参考。 如果你有一个refcounted数据成员,并返回它可能会很好。
如果您希望调用者获得副本,请执行1)。
我的经验法则是返回一个简单的基本数据types,如int,string等的副本。复杂的结构可能会更昂贵(像你提到的向量),我更喜欢返回一个const引用。
在这种情况下,编译器将无法执行(N)RVO。 (named)返回值优化是一种优化,其中编译器在返回值的位置创build函数自动variables以避免需要复制:
std::string f() { std::string result; //... return result; }
当编译器看到上面的代码(并假设如果有其他的返回值,它也会返回result
variables),它知道variables的result
只有可能的命运被复制到返回的临时然后被销毁。 编译器可以完全删除result
variables,并使用返回临时variables作为唯一的variables。 我坚持:编译器不会删除返回临时的,它将删除本地函数variables。 需要临时返回来完成编译器调用约定。
在返回类的成员时,成员必须存在,并且调用约定要求返回的对象位于特定的位置(通常是堆栈地址)。 编译器不能在返回的对象位置上创build方法属性,也不能避免复制。
我正在返回一个参考,因为一个string似乎不是“便宜地复制”给我。 这是一个复杂的数据types,具有dynamic内存pipe理等等。
“如果你想要调用者得到一个副本,你应该返回值”的争论是没有意义的,因为它并不妨碍副本。 来电者仍然可以执行以下操作并获取副本
string s = obj.str();
你需要在调用者端明确地创build一个引用,以便能够直接引用数据成员 – 但为什么你会这样做? 肯定有足够的用户定义的types,便宜的复制
- 智能指针
- 迭代器
- 所有的非类types。
作为公共接口的一部分返回对象内部的引用,如果不是完全不好的devise,可能会产生代码异味。
在返回对公共接口中的内部对象的引用之前,devise者应该暂停。 这样做可以让class级的用户成为您devise的一部分。 通常这是不必要的,有时候表示需要进一步的devise工作。 正如评论者所指出的,有时候这是必要的。
如果没有特殊的理由使用一个值types作为返回值,我总是返回一个const引用。 如果我需要(或期望需要)一个(可写)副本,我添加一个副本ctor和一个赋值运算符到返回的类如果尚不可用。 用法思考:
const MyClass & ref = container.GetAt( 1234 ); // need only reference MyClass copy = container.GetAt( 1234 ); // get writable copy
其实这很简单,不是吗?
-
如果它是一个小的基本types – 像int和long这样的主要types,以及它们的包装器和其他基本的东西,如“Point”,则返回一个副本
-
如果它的string或任何其他复杂types – 返回一个引用。
我唯一遇到的问题是返回一个const引用,这是我通常为非基本types所做的事情,就是没有任何东西可以阻止调用者去除“const”,然后修改这个值。
就个人而言,我build议这样的代码是一个错误。 如果他们知道你正在返回一个引用,并继续抛出const,那么这就是他们的头。