|
C++的那些事——临时量生存期延长的坑
提问环节
开始之前,先进入提问环节,看下面的代码是否存在问题?
代码一:
int&& GetObj() {
int a;
return std::move(a);
}
auto ref = GetObj();
代码二:
int&& GetObj(const int& a) {
return std::move(a);
}
auto ref = GetObj(10);
代码三:
struct A {
int& a;
};
auto ptr = new A(10);
int b = ptr->a; // 使用变量a
代码四:
struct A {
int& a;
};
A obj1{ 10 };
A obj2(20); // C++20起,允许圆括号的初始化
正式介绍
在C++中,存在这么一条法则:引用一旦绑定到临时变量上,则临时变量的生成期会被延长到引用的生存期。
这里说的引用不区别是左值引用还是右值引用。 什么意思呢?举例说明:
const int& a = 10; // 字面值10的生命周期被延长到与引用变量a相同。
class A {
public:
A() {
cout << &#34;construct&#34; << enld;
}
~A() {
cout << &#34;destruct&#34; << endl;
}
}
A&& ref = A(); // 创建了一个临时对象A, 此时它的生命期被延长的与ref变量生存期相同。
上面的概念很好理解,但是要注意它的核心原则是: 临时量的生存期的延长只与它直接绑定的引用变量相关联,如果使用绑定了该临时量的引用初始化的第二引用时,第二个引用生命周期不会不影响临时量的生存期,不存在传递性。
正因为上面的原因,下面的几个场景会导致产生悬挂引用的风险,代码BUG很难定位,对于刚入门的开发同事,是一个天坑:
- return 语句中绑定到函数返回值的临时量不被延续:它立即于返回表达式的末尾销毁。这种函数始终返回悬垂引用。
- 在函数调用中绑定到函数形参的临时量,存在到含这次函数调用的全表达式结尾为止:若函数返回一个引用,而其生命长于全表达式,则它将成为悬垂引用。
- 绑定到 new 表达式中所用的初始化器中的引用的临时量,存在到含该 new 表达式的全表达式结尾为止,而非被初始化对象的存在期间。若被初始化对象的声明长于全表达式,则其引用成员将成为悬垂引用。
- 绑定到用直接初始化语法(括号),而非列表初始化语法(花括号)初始化的聚合体的引用元素中的引用的临时量,存在直至含该初始化器的全表达式末尾为止。
而文章开头举的四个例子,正好对应了上面的四种场景。
揭晓答案
代码一:
int&& GetObj() {
int a;
return std::move(a);
}
auto ref = GetObj();
答案: 存在问题,返回局部变量的引用,悬挂引用。
....
接下来,剩余的内容去视频中继续看。。 (兄弟们实在对不住了,我B站UP主视频播放量太少了,经常个位数,靠兄弟们涨点人气,只能麻烦兄弟们过去给点个赞,投个币啥的) |
|