我有一个函数返回对我的类“record”的实例的引用。
record& get_record(int key) {
return lookup(key);
}
这是有效的,它返回引用而不是值。现在我稍微修改一下。
record& get_record(int key) {
if (valid(key))
return lookup(key);
else {
record x;
x.valid=false;
return x; //Here I really want to return a temporary variable
// and not a reference to a local variable.
}
}
返回对局部变量的引用是不是一个好主意?以及如何以一种临时变量的方式返回x?
答案 0 :(得分:7)
返回对局部变量的引用是不是一个好主意?
是。离开函数时,本地对象将被销毁,因此返回的引用总是悬空。
您可以将x
变为static
变量。
record& get_record(int key) {
if (valid(key))
return lookup(key);
else {
static record x;
x.valid=false;
return x;
}
}
请注意,返回的引用将始终引用同一个对象,即x
。
答案 1 :(得分:6)
这比一个坏主意it is undefined behavior更糟糕,导致大多数情况发生崩溃。这很糟糕(TM)。
您可以做的是更改get_record
的返回类型,以便返回智能指针。如果key
有效,则返回指向它的观察者指针。否则,它返回一个拥有一个新创建的对象的智能指针:
#include <memory>
#include <iostream>
struct record { int n; } some_record{42};
std::shared_ptr<record> get_record(bool b)
{
if (b == true) {
return std::shared_ptr<record>{&some_record, [](record*){}}; // see explanation ^1
}
return std::shared_ptr<record>{new record{0}};
}
int main()
{
std::cout << get_record(true)->n << "\n"; // this is some_record
std::cout << get_record(false)->n << "\n"; // this is a temporary
}
1)关于[](record*){}
:当智能指针被销毁时,将调用作为std::shared_ptr::shared_ptr()
的第二个参数给出的no-op lambda。它取代了std::shared_ptr
的默认删除器,其行为是在拥有指针上调用delete
。
关于您的设计存在缺陷的原因。实际上,使get_record
返回引用会使其不一致。你想要的是:
key
有效,则返回对现有/永久对象的引用,这两者是相互排斥的,你的功能没有意义:get_record
在语义上有什么回报?
答案 2 :(得分:1)
如果允许修改 get_record 功能,可以将返回类型更改为指向记录而不是引用记录。
<a href="javascript:history.go(-1)" class="back" >
<asp:Label ID="backLabel" runat="server" Text="Back" meta:resourcekey="backLabelResource1" />
</a>
但是,这种方法需要两个保证:
答案 3 :(得分:0)
正如其他人已经详细说明为什么返回对本地的引用是坏的,生病只是提供另一种选择:例外。虽然您可以编写自定义异常,但可能会出现std::out_of_range
异常(因为密钥位于有效密钥范围之外,这正是std::map::at
所做的)。
查看:How to throw a C++ exception
record& get_record(int key)
{
if (valid(key))
{
return lookup(key);
} else {
throw std::out_of_range("The provided key is invalid");
}
}
显然,您现在必须在调用get_record
的代码中捕获异常,否则您的程序仍将终止。
答案 4 :(得分:0)
返回引用本身不会产生未定义的行为,但如果您尝试修改它,那么您将会这样做。
访问其生命周期之外的对象是未定义的行为。
int* foo(void) {
int a = 17; // a has automatic storage duration
return &a;
} // lifetime of a ends
int main(void) {
int* p = foo(); // p points to an object past lifetime ("dangling pointer")
int n = *p; // undefined behavior
}
http://en.cppreference.com/w/c/language/lifetime
如果您可以访问C ++ 17,则可以使用std::optional实现它。请注意使用std::reference_wrapper,因为在std::optional
中使用引用会使您的程序格式不正确。
std::optional<std::reference_wrapper<record>> get_record(int key) {
if (valid(key))
return std::optional<std::reference_wrapper<record>>(lookup(key));
else
return std::nullopt;
}
如果没有C ++ 17,您只需返回指向记录的指针:
record* get_record(int key) {
if (valid(key))
return &lookup(key);
else
return nullptr;
}
或者如果您愿意,可以保留引用返回类型,并抛出异常以指示缺少记录。虽然这是我最不喜欢的方法,因为它可以轻松调用get_record
而无需包裹try / catch
。
record& get_record(int key) {
if (valid(key))
return &lookup(key);
else
throw std::out_of_range("Invalid record key!");
}