将 const 引用返回到本地对象时究竟会发生啥?
Posted
技术标签:
【中文标题】将 const 引用返回到本地对象时究竟会发生啥?【英文标题】:What exactly happens when returning const reference to a local object?将 const 引用返回到本地对象时究竟会发生什么? 【发布时间】:2012-07-31 19:33:38 【问题描述】:struct A
A(int) : i(new int(783))
std::cout << "a ctor" << std::endl;
A(const A& other) : i(new int(*(other.i)))
std::cout << "a copy ctor" << std::endl;
~A()
std::cout << "a dtor" << std::endl;
delete i;
void get()
std::cout << *i << std::endl;
private:
int* i;
;
const A& foo()
return A(32);
const A& foo_2()
return 6;
int main()
A a = foo();
a.get();
我知道,返回对本地值的引用是不好的。但是,另一方面,const 引用应该延长一个临时对象的生命周期。
此代码产生一个 UB 输出。所以没有延长寿命。
为什么?我的意思是有人可以逐步解释发生了什么吗?
我的推理链哪里出错了?
foo():
A(32) - 演员
return A(32) - 创建并返回对本地对象的 const 引用
A a = foo(); - a被foo()返回值初始化,返回值超出范围(out of expression)被销毁,但是a已经被初始化了;
(但实际上析构函数是在复制构造函数之前调用的)
foo_2():
return 6 - 隐式创建类型 A 的临时对象,创建对该对象的 const 引用(延长其寿命)并返回
A a = foo(); - a被foo()返回值初始化,返回值超出范围(out of expression)被销毁,但是a已经被初始化了;
(但实际上析构函数是在复制构造函数之前调用的)
【问题讨论】:
"const 引用应该延长一个临时对象的生命周期" 不扩展它. 我认为这就是亚历山大在延长寿命方面所说的:herbsutter.com/2008/01/01/… @Giel:不。常量引用可以延长临时对象的生命周期。在使用临时对象时,常量引用和非常量引用完全不同。在这种情况下,它的工作方式与 OP 似乎期望的不同。 @AndreyT:对,vmpstr 给出的链接很好地解释了它。然而,在 OP 的情况下,reference 是返回的临时对象,而不是引用的对象。因此,正如预期的那样,它不会将临时的生命周期延长到产生它的范围之外。我相信后者在所有情况下仍然是正确的:局部对象(临时或作为局部变量)不超出范围。 不,他们的生活不会超出他们的范围,但他们的尸体通常仍然在那里并且“似乎”正在运作和运作,这可能会令人困惑,因为返回参考“似乎工作正常“在某些情况下,但在其他情况下则不然。虽然这是一些额外的工作,但为了防止这种混淆(以及使用过时指针的混淆),我喜欢在删除它们后将析构函数中的成员指针清空,并且在某些情况下将成员变量设置为安全的无效状态,所以如果有人尝试使用已删除的对象,这很明显。 【参考方案1】:语言规范中明确规定了每个特定上下文的临时生命周期延长规则。它说
12.2 临时对象
5 第二个上下文是引用绑定到临时的。 [...] 临时绑定到函数返回语句中的返回值 (6.6.3) 一直持续到函数退出。 [...]
您的临时对象在函数退出时被销毁。这发生在接收者对象的初始化开始之前。
您似乎认为您的临时工应该以某种方式活得比这更长。显然,您正在尝试应用这条规则,即临时文件应该一直存在到完整表达式结束。但该规则不适用于在函数内部创建的临时对象。此类临时人员的生命周期由他们自己的专用规则管理。
如果有人尝试使用返回的引用,您的 foo
和 foo_2
都会产生未定义的行为。
【讨论】:
但是如果“在函数返回语句 (6.6.3) 中的临时绑定到返回值一直持续到函数退出” foo_3()return A( 54);? @Alexander - 不,A foo_3()
返回值的副本。复制的值不会在函数结束时销毁。当您返回一个引用时,该引用仍然存在 - 它不再引用任何内容。
我明白了.. 但是应该在表达式 A a = foo_3(); 中调用复制 ctor 两次第一次从临时本地复制返回值,第二次初始化 A a。但它只调用一次。还是只是优化?
@Alexander:当你使用A foo_3()return A(54);
时,你有两个不同的概念临时性。第一个是您明确创建的 - A(54)
。第二个是一个特殊的内部“过渡”临时文件,它在函数退出后保存结果。 A(54)
临时被复制到“过渡”临时,A(54)
被销毁。 “过渡”临时对象的寿命更长,可用作接收对象的初始值设定项。
对于A a = foo();
,如果返回的对象被销毁,那么分配给a的是什么?它指的是哪里?【参考方案2】:
您误解了“直到函数退出”。如果您真的想使用 const 引用来延长对象的寿命,超出foo
,请使用
A foo()
return A(32);
int main()
const A& a = foo();
你必须从foo
返回按值,然后使用一个常量引用来引用返回值,如果你想以你期望的方式扩展东西。
正如@AndreyT 所说,对象在具有const &
的函数中被销毁。您希望您的对象在foo
之后仍然存在,因此您应该不拥有const &
(或&
)在foo
或foo
的返回类型中的任何位置。第一次提到const &
应该在main
中,因为那是应该让对象保持活动状态的函数。
您可能认为这个按值返回的代码很慢,因为在返回中似乎有 A 的副本,但这是不正确的。在大多数情况下,编译器只能在 A 的最终位置(即调用函数的堆栈上)构造一次,然后设置相关引用。
【讨论】:
以上是关于将 const 引用返回到本地对象时究竟会发生啥?的主要内容,如果未能解决你的问题,请参考以下文章
当我设置 IIS 池的 LoadUserProfile 时究竟会发生啥?
当您引用一个 DOM 对象 (var o=docume...) 并通过 parent.innerHTML='' 删除该 DOM 对象时会发生啥?