我知道为什么下面的内容不起作用,所以我不是在问为什么。但我感觉很糟糕,在我看来这是一个非常大的编程障碍。
#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 ++做什么?
感谢。
答案 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!