2010-01-11 57 views
3

非模板类,没有任何理由,更喜欢函数的返回签名的形式const <type>& foo();<type> foo();?其中<type>是一种内在类型。那么如果<type>是一个类/结构对象呢?常量<type>和富()与<type> FOO()

的功能是否是常量也有兴趣有差别:const <type>& foo() const;<type> foo() const;

例如在非模板类,非模板函数:

const int& foo() const { return member_variable_; } 

对战

int foo() const { return member_variable_; } 
+1

重复:HTTP://计算器.com/questions/494756/when-you-you-return-a-reference-to-a-object-from-a-class-method – 2010-01-11 21:55:51

+0

@Stefan细微差别,你会注意到我的问题是标记优化..你发现的另一个问题是询问最佳实践。 – paxos1977 2010-01-12 16:46:46

回答

0

如果需要返回一个值,你必须返回一个值 - const引用是不是一个可以接受的替代品。但我怀疑你是在问这样的事情:

struct A { 
    X x; 
    ... 
    X f1() { return x; } 
    const X & f2() { return x; } 
}; 

我从来没有想出一个这样的万无一失的指导方针。

+0

@Neil:对于内在类型的情况更感兴趣,在你的例子中X是一个整数。(为什么值得我倾向于返回值) – paxos1977 2010-01-11 21:39:23

+1

我认为,使用'auto',通过引用返回会变得越来越危险。现在像'auto x = a.f2()'会导致'x'静静地成为一个引用,如果'a'在'x'之前被破坏(如果它实际上是一个智能指针,这很有可能),我们得到一个悬而未决的参考。国际海事组织,除非意图是要返回一个引用(这可以通过返回一个[可能是聪明的]指针来更好地突出显示),只需返回值,并让RVO开始。 – 2010-01-11 21:41:11

+1

哦,是的,同样,在C++ 0x中的移动语义对于具有移动构造函数的任何类型(涵盖所有标准类型,对于任何昂贵的 - 复制用户定义类型定义一个类型),对于按值返回的perf命中将会忽略不计, 。 – 2010-01-11 21:42:10

0

这一切都取决于你想要做什么。

如果你想引用返回一个对象由用户代码中使用,则:

<type>& foo() { ... } 

如果你想引用返回一个对象,但只读方式,允许只有进入此对象类型的const函数,则:

const <type>& foo() { ... } 

如果你想引用返回一个对象由用户代码中使用,即使类的实例是只读的使用(如常量)然后:

<type>& foo() const { ... } 

如果您希望以只读方式返回对对象的引用,只允许访问此对象类型的常量函数,并且如果类实例以只读方式使用(如const),则:

const <type>& foo() const { ... } 

只需添加一个警告:不要返回函数的变量的引用,它们不是活着出来的功能...

+1

我不得不说,我非常强烈地说:“这就是说,这种功能(但不是所有情况下)的良好实践......” - 这只是极少数情况下的良好实践 - 操作符[]用于容器是我能想到的唯一例子。 – 2010-01-11 21:41:53

+0

好吧,我将删除这个rec​​omandation,因为我觉得不够有经验,但我不得不说我经常使用与此方案相匹配的accessor-like成员函数,使用const-correctness使整个只读访问系统可能没有通过使用非const成员函数打破它。也就是说,我可能没有足够的经验来看到这个问题。 – Klaim 2010-01-11 21:51:47

+0

仅仅因为你有一个类的非const实例(或引用)并不意味着你应该有对其数据成员的无限制访问。如果您完全依赖您正在访问的实例的常量,那么最好不要使用成员函数并使用普通结构。 – 2010-01-11 21:56:14

0
const <type>& foo(); 

你可能有相似元素的容器资源在你的班级和foo()返回特定元素,如果您知道资源可用时间足够长,则可以返回对容器元素的const引用,这样可以避免不必要的数据分配/副本。 它可能是const或者不是,取决于你如何处理数据,如果你不想通过foo()返回的引用修改数据,那么使它成为const。也许你有一个情况,你想修改资源和一些额外的工作需要完成,你会使非const。你可以让你的api用户都选择基于用例。

<type> foo(); 

返回资源<type>的新副本。这意味着一个副本。这对于简单的数据很有用,或者可以共享值,并且您想要确保<type>的每个实例都是单独的 - 非共享像某些字符串或int。

0

如果这个类型是一个内在类型,那么绕过const refs不会提高性能,并且可能会降低它的性能,也会降低安全性;最好是喜欢传球和按价值回归。我不是一个ASM人,但据我了解,通过引用返回意味着该值无法存储在寄存器中,因为它需要返回地址,因此可以禁用一些其他有用的优化;尽管如此,我对此并不积极。

如果它是一个类或结构,它取决于值的生命周期;如果它是本地的函数,那么它不能被返回,否则,如果它是一个类成员,我更喜欢常量类型&返回,因为它可以通过避免复制构造函数提供更好的性能。 (意见不同,因为价值被认为更安全)。它还可以让你不必执行复制构造函数或使用潜在的恶意默认值,这很好。

3

对于原始类型,只需按值返回即可。没有理由通过const引用返回一个原语。

对于非原始类型,它取决于。

如果返回的值是函数的局部变量,则必须按值返回。否则,在调用者使用返回的引用之前,该变量将被销毁。

如果返回的值是类数据成员,那么您可以选择。

通过const引用返回避免了副本,这可能是一个性能优势。但是,它将您的接口绑定到实现,即数据成员和返回值必须是相同的类型。这也是不太安全的,因为主叫方可以保留参考长于对象的生命周期,如:

const X& x = y->foo(); 
delete y; 
// use x, oops! 

返回的值会带来一个或两个拷贝,这取决于你是否能取返回值优化的优势(这很可能是像这样简单的get()方法)。另外,使用默认拷贝构造函数的对象副本可以更高效地复制(想想:编译器可以简单地对数据进行memcpy)。

建议:只有在确定该调用/复制被认为是性能问题后,才可以按值返回,然后切换到仅由const引用返回。

最后,另一种选择是通过参数返回,这对于更复杂的对象(例如,字符串)有用:

void foo(X& return_val) { return_val = member_variable_; } 

或:

void foo(X* return_val) { 
    assert(return_val != 0); 
    *return_val = member_variable_; 
} 
+0

'使用default ... memcpy'=>的对象副本不太完美。该标准允许编译器在所谓的返回值优化中省略副本。 'T f(){T tmp;/*操作* /返回tmp; }编译器允许做的是在分配给返回值的内存中创建'tmp'变量,并避免在return语句中复制该变量。编译器不会将'memcpy'对象从一个位置移动到另一个位置。 – 2010-01-12 00:09:36

+0

我的memcpy评论不是指返回值优化,而是指向POD对象与间接副本的副本。例如,请参阅http://docs.sun.com/app/docs/doc/820-7599/bkahu?a=view。 – 2010-01-12 01:22:14

相关问题