C++ 从函数返回对局部变量的常量引用
关于从函数返回对局部变量的引用,我有一些问题:C++ 从函数返回对局部变量的常量引用,c++,reference,undefined,const-reference,C++,Reference,Undefined,Const Reference,关于从函数返回对局部变量的引用,我有一些问题: class A { public: A(int xx) : x(xx) { printf("A::A()\n"); } }; const A& getA1() { A a(5); return a; } A& getA2() { A a(5); return a; } A getA3() { A a(5); return a; }
class A {
public:
A(int xx)
: x(xx)
{
printf("A::A()\n");
}
};
const A& getA1()
{
A a(5);
return a;
}
A& getA2()
{
A a(5);
return a;
}
A getA3()
{
A a(5);
return a;
}
int main()
{
const A& newA1 = getA1(); //1
A& newA2 = getA2(); //2
A& newA3 = getA3(); //3
}
我的问题是=>
getA1()
的实现是否正确?
我觉得它不正确,因为它返回的是局部变量或临时变量的地址main
(1,2,3)中的哪些语句将导致未定义的行为const A&newA1=getA1()中代码>该标准是否保证在引用超出范围之前,由常量引用绑定的临时文件不会被销毁
问题1:是的,这是一个问题,请参见问题2的答案 Q2:1和2是未定义的,因为它们引用了getA1和getA2堆栈上的局部变量。这些变量超出范围,不再可用,更糟糕的是,当堆栈不断变化时,这些变量会被覆盖。getA3工作的原因是创建了返回值的副本并将其返回给调用者 问题3:不存在此类保证来查看问题2的答案 一,。
getA1()
实现是否正确?我觉得它是不正确的,因为它返回的是局部变量或临时变量的地址
程序中唯一正确的getAx()
版本是getA3()
。无论以后如何使用,其他两个都有未定义的行为
二,。main(1,2,3)中的哪个语句会导致未定义的行为
从某种意义上说,它们都不是。对于1和2,未定义的行为是函数体的结果。对于最后一行,newA3
应该是编译错误,因为您无法将临时引用绑定到非常量引用
三,。在const A&newA1=getA1()中代码>标准是否保证受const
在引用超出范围之前,引用不会被销毁
不可以。以下是一个例子:
A const & newConstA3 = getA3 ();
这里,getA3()
返回一个临时变量,该临时变量的生存期现在绑定到对象newConstA3
。换句话说,在newConstA3
超出范围之前,临时文件将一直存在。我认为主要问题是您根本没有返回临时文件,您应该
return A(5);
而不是
A a(5);
return a;
否则,您将返回局部变量地址,而不是临时的。临时到常量的引用仅适用于临时对象
我想这里可以解释一下:
如果您将在VC6上编译此文件,您将收到此警告
******编译器警告(1级)C4172
局部变量或临时变量的返回地址
函数返回局部变量或临时对象的地址。局部变量和临时对象在函数返回时被销毁,因此返回的地址无效。*****
在测试这个问题时,我发现了一件有趣的事情(给定的代码在VC6中工作):
class-MyClass
{
公众:
MyClass()
{
objID=++cntr;
}
MyClass&myFunc()
{
MyClass obj;
返回obj;
}
内部目标;
静态int-cntr;
};
int MyClass::cntr;
main()
{
MyClass tseadf;
库蒂不认为这是正确的。临时概念根本不通过返回类型传播。考虑到你可能在不同的翻译单元中调用函数,而调用方无法知道返回表达式是否为临时表达式。@我认为我没有明确指出我的错误。问题在于函数。返回引用的签名。return语句中的元素是否为临时元素并不影响问题中代码的不正确性……现在,随着您所做的更改:函数不再返回引用,而是返回值对象,那么代码就变为正确的。区别在于临时元素在调用函数将绑定到调用函数中的引用,并延长其生存期,而带有初始签名的临时函数位于被调用函数中。想想看:在调用时,编译器不知道返回的常量引用是否是对临时函数的引用……如果编译器允许的话要绑定临时对象,它必须在调用激活框架内为真实对象(而不是引用)保留额外空间,而且它甚至不知道对象的大小。请考虑:'struct X{};struct Y:public X{int ch;};struct Z:public X{double d;};X const&f();void g(){X const&xr=f();}'现在,编译器如何在不知道f()的情况下定义决定返回对象引用的实际类型是什么?它如何确定要调用的析构函数?…或者必须为未知临时对象保留多少额外堆栈空间,或者即使返回的对象是临时对象还是非临时对象。--回到您的示例,您返回的是一个变量,代码在将其复制到返回变量,然后将其绑定到调用方函数中的引用并不意味着语义不是副本的语义。您可以通过将X的副本构造函数声明为private来检查它。您的代码将不会编译。我认为最后一条语句令人困惑。您的意思是,在newConstA3超出范围之前,临时不会被销毁?只需提及您的//3行不应编译,将临时绑定到非常量引用是标准所禁止的。Visual Studio允许它,但它是错误的,gcc不会原谅您。这是@sbk的一个好观点。在本链接的第2节中对此进行了解释:试试这个:MyClass&o=tseadf.myFunc();非常感谢您的回复,关于VC6,我们只限于管理人员:)
class MyClass
{
public:
MyClass()
{
objID=++cntr;
}
MyClass& myFunc()
{
MyClass obj;
return obj;
}
int objID;
static int cntr;
};
int MyClass::cntr;
main()
{
MyClass tseadf;
cout<<(tseadf.myFunc()).objID<<endl;
}