我对返回
const
对临时变量的引用的函数声明有些困惑。
在下面的代码中
#include <string>
#include <iostream>
using namespace std;
const string& foo() {
return string("foo");
}
string bar() {
return string("bar");
}
int main() {
const string& f = foo();
const string& b = bar();
cout << b;
}
方法
foo
和bar
有什么区别?
为什么
foo
给我warning: returning reference to local temporary object [-Wreturn-stack-address]
。不是在const string& f = foo();
上创建的临时副本吗?
string("foo")
在函数中本地创建一个 std::string
类型的对象,其中包含值 "foo"
。该对象将在函数结束时被销毁。因此,一旦代码离开该函数,返回对该对象的引用将无效[1]。因此,在 main
中,您永远不会拥有对该字符串的有效引用。如果您在 foo
中创建了一个局部变量,情况也是如此。
返回引用的全部要点是您不创建副本,并且初始化引用(
string &f = foo()
是初始化)不会创建原始对象的副本 - 只是对同一对象的另一个引用[这是当代码返回到 main
] 时已经无效。对于许多事物来说,引用可以被视为“同一事物的不同名称”。
引用对象(换句话说,“别名”引用的实际对象)的生命周期应始终比引用变量的生命周期长(在本例中为
f
)。
在
bar
的情况下,代码将创建一个副本作为 return string("bar");
的一部分,因为您返回该对象而不获取其引用 - 换句话说,通过复制该对象,这样就可以了。
[1] 迂腐地,虽然仍在函数内部,但在函数内编写的代码结束后,在编译器引入的代码中处理函数中创建的对象的销毁。
不是在
创建的临时副本吗const string& f = foo();
你从哪里听说的?
将
const
添加到引用中通常可以使初始化的临时对象的生命周期延长到引用本身的生命周期。从来没有副本。
但这里的情况并非如此。您绑定到引用的对象在函数末尾超出范围,并且优先于其他所有内容。
您正在返回一个悬空引用;期间。
为什么 foo 给我警告:返回对本地临时的引用 对象 [-Wreturn-stack-address]。
您正在
foo()
中创建一个临时字符串对象,并且返回对该对象的引用,该对象将立即超出范围(悬空引用)。const string& foo() {
return string("foo"); // returns constant reference to temporary object
} // object goes out of scope
bar()
完全不同:
string bar() {
return string("bar"); // returns a copy of temporary string
}
...
const string& b = bar(); // reference an rvalue string
方法 foo 和 bar 之间有什么区别?
foo()
返回常量(悬空)引用,而 bar()
返回临时字符串对象的副本。
在这两种情况下,都会在堆栈上初始化并分配字符串对象。 从函数返回后,包含它的内存段变得无关紧要,并且其内容可能被覆盖。
功能区别:
bar
函数返回在其中创建的字符串实例的副本。
foo
函数返回对其内部创建的字符串实例的引用。
换句话说,它返回一个指向其返回值的隐式指针,该指针驻留在临时内存段中 - 这就是您发出警告的原因。