返回作为const引用的参数的问题

时间:2009-12-03 01:08:30

标签: c++ pass-by-reference

我知道为什么下面的内容不起作用,所以我不是在问为什么。但我感觉很糟糕,在我看来这是一个非常大的编程障碍。

#include <iostream>
#include <string>
using namespace std;

string ss("hello");

const string& fun(const string& s) {
        return s;
}

int main(){
        const string& s = fun("hello");
        cout<<s<<endl;
        cout<<fun("hello")<<endl;
}

第一个cout不起作用。第二个cout会。

我关注的是:

  

是不是可以想象一个方法实现者想要返回一个const引用且不可避免的参数的情况?
  我认为这是完全可能的。
  在这种情况下你会用C ++做什么?

感谢。

8 个答案:

答案 0 :(得分:5)

在C ++中,建立对象的生命周期很重要。一种常见的技术是确定每个对象的“所有者”。所有者负责确保对象在需要时存在,并在不需要时将其删除。

通常,所有者是另一个将拥有的对象保存在实例变量中的对象。处理此问题的其他典型方法是使其成为类的全局,静态成员,局部变量或使用引用计数指针。

在您的示例中,没有明确的字符串对象所有权。它不归main()函数所有,因为它不是局部变量,也没有其他所有者。

答案 1 :(得分:3)

该技术有效且一直使用。但是,在您的第一个示例中,您正在将const char*转换为临时 std::string并尝试返回它,这与将const-reference返回到存储的对象不同别处。在第二个示例中,您正在执行相同的操作,但是在临时销毁之前您正在使用结果,在这种情况下这是合法但危险的(请参阅您的第一个案例。)

更新:请允许我澄清一下我的答案。我说问题在于临时的创建,而不是正确处理正在创建的对象的生命周期。该技术是一个很好的技术,但它(以及许多其他好的技术)需要满足功能的前后条件。部分负担落在功能程序员(应该记录它)上,部分负责客户端。

答案 2 :(得分:3)

我感觉到你的痛苦。我发现其他情况,返回const引用似乎是正确的事情,但有其他丑陋的问题。

幸运的是,这个微妙的陷阱在c ++ 0x中解决了。始终按价值返回。新的移动构造函数将使你的事情变得更快。

答案 3 :(得分:2)

我认为这是C ++的一个小弱点。有两个因素的不幸结合:

  • 只要参数为。
  • ,函数的返回才有效
  • 隐式转换意味着函数的参数不是它可能看起来的对象。

我对那些没有想到他们有指针/引用的对象的生命周期的人没有同情。但隐式转换肯定是具有微妙优点和缺点的语言特征,并不能使分析变得非常容易。有时隐式转换是坏消息,这就是explicit关键字存在的原因。但问题不在于转换到string一般都不好,这对于这个以不正确的方式使用的函数来说是不好的。

该函数的作者实际上可以通过定义重载来禁用隐式转换:

const char *fun(const char *s) { return s; }

这种改变本身意味着以前不好的代码有效。所以我认为在这种情况下这样做是个好主意。当然,如果某人定义了fun的作者从未听说过的类型,并且有operator std::string()的类型,则无济于事。此外,fun不是一个现实的函数,对于更有用的例程,您可能不希望提供在char*上运行的等价函数。在这种情况下,void fun(const char *);至少会强制调用者显式地转换为字符串,这可能有助于他们正确使用该函数。

或者,来电者可以注意到他正在提供char*,并返回对string的引用。在我看来,这是一个免费的午餐,所以警报响应应该响起这个字符串来自哪里,以及它会持续多长时间。

答案 4 :(得分:0)

是的,我同意在某些情况下这是一个相关的问题。

我会使用引用计数指针来“解决”它。

答案 5 :(得分:0)

  

是不是可以想象一个方法实现者想要返回一个const引用并且不可避免的参数的情况?

错误的问题,真的。您所要做的就是包括返回的引用是否可以是参数(通过引用传递),以及文档作为接口的一部分。 (这通常也很明显。)让调用者决定做什么,包括将临时对象变成一个显式对象,然后传递它。

通常和必需来记录返回指针和引用的生命周期,例如std :: string :: data。

  

在这种情况下你会用C ++做什么?

通常你可以通过值传递而不是。这通常用于std :: copy(在本例中为目标迭代器)。

答案 6 :(得分:0)

在即将推出的C ++标准中,r值引用可用于保持临时对象“活着”并解决您遇到的问题。

您可能还希望查找完美的转发和移动构造函数。

答案 7 :(得分:0)

我认为你在C ++ 98中遇到麻烦:)

这可以通过两种方式解决。首先,您可以使用共享指针。在这种情况下,内存将由shared_ptr自动管理,您就完成了!但是,在大多数情况下这是一个糟糕的解决方案。因为你真的不是共享许多引用之间的内存。如果您考虑始终使用堆,auto_ptr是此问题的真正解决方案。 auto_ptr需要一点关键改进,这在C ++ 98中是不可用的,即:移动语义!

更好的解决方案是允许在引用之间移动所有权,方法是使用C值0x中的r值引用。所以,你的代码看起来像(不确定语法是否正确):

string fun(const string& s) {
        return s; // return a copy of s
}
....
string s = fun("Hello"); // the actual heap memory is transfered to s.
                         // the temporary is destroyed, but as we said
                        // it is empty, because 's' now owns the actual data!