ant*_*_rh 18 c++ reference this this-pointer
返回对此对象的引用通常用于赋值运算符重载.它还用作命名参数idiom的基础,它允许通过对setter方法的调用链初始化对象:Params().SetX(1).SetY(1)每个都返回对*this的引用.
但是返回引用是否正确*this.如果我们调用该方法为临时对象返回对此的引用,该怎么办:
#include <iostream>
class Obj
{
public:
Obj(int n): member(n) {}
Obj& Me() { return *this; }
int member;
};
Obj MakeObj(int n)
{
return Obj(n);
}
int main()
{
// Are the following constructions are correct:
std::cout << MakeObj(1).Me().member << std::endl;
std::cout << Obj(2).Me().member << std::endl;
Obj(3).Me() = Obj(4);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
是的,返回*这是安全的.简单的情况是,这不是暂时的,但即使它是,这应该是可能的:
临时对象作为评估全表达式(1.9)的最后一步被销毁,该表达式(词法上)包含创建它们的点.即使该评估以抛出异常结束也是如此(C++03§12.2/ 3).
换句话说,直到你达到分号,一切都应该没问题(理论上).
所以下面的代码应该工作:
std::cout << MakeObj(1).Me().member << std::endl;
Run Code Online (Sandbox Code Playgroud)
虽然这不起作用:
const Obj &MakeMeObj(int n) { return Obj(n).Me(); }
std::cout << MakeMeObj(1).member << std::endl;
Run Code Online (Sandbox Code Playgroud)
这是合乎逻辑的,因为您返回对临时的引用.大多数编译器都会对此发出警告/错误,但如果您的代码变得复杂,则需要注意这一点.
就个人而言,我会阻止在临时对象上调用这些方法来强制API用户考虑对象的生命周期.可以通过重载方法来完成:(如果您的编译器已经支持它)
Obj &Me() & { return *this; }
Obj &Me() && = delete;
Run Code Online (Sandbox Code Playgroud)
Run Code Online (Sandbox Code Playgroud)// Are the following constructions are correct: std::cout << MakeObj(1).Me().member << std::endl; std::cout << Obj(2).Me().member << std::endl;
是的,因为在每一行中,所有临时对象的生存期都得到了扩展,以考虑到完整的表达式。
正如cppreference.com所说:
(...)所有临时对象都被销毁,这是评估(按词法)包含创建它们的位置的完整表达式(...)的最后一步。
如果尝试拆分完整表达式,则(希望)会收到编译器错误或警告:
// not allowed:
Obj& ref = MakeObj(1);
std::cout << ref.Me().member << std::endl;
Run Code Online (Sandbox Code Playgroud)
在其他情况下,编译器可能不够聪明,无法看到问题,没有给出任何诊断消息就创建了可执行文件,并最终在程序中构建了未定义的行为:
// undefined behaviour:
Obj &ref = MakeObj(1).Me();
std::cout << ref.member << std::endl;
Run Code Online (Sandbox Code Playgroud)