看看这段代码:
class Test
{
//
};
Test TestAddress()
{
Test test;
cout << "Object Address in function: " << (int)&test << endl;
return test;
}
int IntAddress()
{
int test;
cout << "Integer Address in function: " <<(int)&test << endl;
return test;
}
int main() {
int x = IntAddress();
cout << "Integer Address in Main: " <<(int)&x << endl;
Test object = TestAddress();
cout << "Object Address in Main: " <<(int)&object << endl;
return 0;
}
输出结果为:
功能中的整数地址:1076679252
Main中的整数地址:1076679220
功能中的对象地址:1076679221
功能中的对象地址:1076679221
有人可以解释我为什么会这样,当我按值返回一个对象时,我会在函数和main中收到相同的地址。但是当我使用整数做同样的事情时,这个地方是不同的?
答案 0 :(得分:5)
我认为您的编译器正在应用return value optimization,其中main()
和TestAddress()
正在内存中的相同的对象变量。
答案 1 :(得分:3)
所谓的&#34;自动变量&#34;在堆栈上分配。 分配的时刻可以进行编译器优化。
当按值返回局部变量时,编译器基本上有两个选择:
=
处,将临时文件复制到接收构造变量的调用者。或者,它可以:
现在,因为&#34;复制整数&#34;和#34;取消一个整数&#34;在CPU处理方面有利于复制(int是CPU原始数字类型),并且因为&#34;临时整数&#34;可以适合CPU寄存器(因此它不是&#34;复制到内存中)编译器使用第一种方法进行int。
由于类可以具有任何大小(并且副本可能具有更高的成本),因此编译器可以决定采用第二种方法。
在任何情况下,您都不应该关心:外部可观察行为将是相同的,因为对外部和内部变量的访问是互斥的。
答案 2 :(得分:1)
首先,您不需要将指针强制转换为int
,因为operator<<()
具有接受void*
打印指针地址的重载。任何指针都可以在不进行转换的情况下传递给<<
(除非针对该指针类型存在专门的重载,例如char*
的指针类型,在这种情况下,您应该转换为void*
而不是int
)。
其次,当函数按值返回对象时,大多数编译器都会实现Return Value Optimization(RVO不用于基本类型,因为副本很便宜)。所以你的代码在幕后的表现基本上更像是这样:
void TestAddress(Test &result)
{
cout << "Object Address in function: " << &result << endl;
}
int main()
{
//...
Test object;
TestAddress(object);
cout << "Object Address in Main: " << &object << endl;
return 0;
}
甚至这个:
void TestAddress(Test &result)
{
new (&result) Test(); // <-- constructor called here instead!
cout << "Object Address in function: " << &result << endl;
}
int main()
{
//...
Test object; // <-- constructor not called here!
TestAddress(object);
cout << "Object Address in Main: " << &object << endl;
return 0;
}
无论哪种方式,编译器都足够聪明,可以知道函数内部的临时对象将最终出现在调用者的对象中,因此它会消除temp并直接作用于调用者的对象。
这就是为什么你看到相同的内存地址由两个输出语句似乎作用于两个独立的对象报告 - 它们实际上是相同使用RVO时内存中的对象。
答案 3 :(得分:0)
Wyjun,
按值返回对象时,这两个对象都包含相同的指针。但是,当您按值返回基元时,函数作用域中的基元将被销毁,其值已经包含在被调用者的堆栈帧中,并且该值将重新分配给新的存储空间。
如果您有任何疑问,请与我们联系!
感谢您的时间,