返回值会发生啥?
Posted
技术标签:
【中文标题】返回值会发生啥?【英文标题】:What happens with return value?返回值会发生什么? 【发布时间】:2015-07-10 21:01:01 【问题描述】:所以我在这个例子中有 2 个类:E
,当它被销毁时打印一条消息 + 可以存储整数值T
,它存储E
的实例并可以返回它通过方法
代码:
class E
public:
E();
~E();
int test;
;
E::E()
E::~E()
std::cout << "E is destroyed" << std::endl;
///////////////////////////////////////////
class T
public:
T();
~T();
E data;
E get();
;
T::T()
T::~T()
E T::get()
return this->data;
///////////////////////////////////////////
int main()
E e;
e.test = 2;
T t;
t.data = e;
E* e2;
e2 = &(t.get());
std::cout << "end of block" << std::endl;
std::cout << e2->test;
运行我的代码的结果是:
E is destroyed
end of block
这意味着E
的某些实例在到达 的末尾之前被销毁
问题:
1.我做了一点实验。 e2 = &(t.get());
行中的 t.get()
值是否正确,因为它没有按值分配给任何变量?
2.如果这个值被破坏,那意味着e2
指针无效。但是std::cout << e2->test;
行仍然打印保存的整数值。指针是否仍然有效,或者这些数据只是偶尔停留在这个内存位置?
【问题讨论】:
是的,你有一个dangling pointer 指向一个超出范围的变量。在此之后尝试对该对象执行任何操作都是未定义的行为。 请注意,如果您的函数具有签名E& T::get()
,您将返回对仍在范围内的data
的引用,这样就可以了。问题是您按值返回了 E
,这是一个立即被销毁的临时值。
@Cyber,是的,我就是这么想的,谢谢。我读过一些关于临时工的东西,但不确定这是否完全是关于我的情况。那么,我对此是否正确:如果临时通过值立即分配给某物,它肯定不会被销毁,是吗?
【参考方案1】:
默认情况下,释放的内存未设置为特定值。因此,它保留了它拥有的最后一个值。发生的情况是您的 t-> 数据 副本 的地址存储在 e2 中。然后,编译器释放所述副本,因为您不再使用它。因此,您可以调用析构函数。
但是,您确实将值 保存在内存中,因此您的指针仍然可以访问它们。 (但我猜想 valgrind 会吐出无效的读取。)
所以是的,您的指针确实无效,但它仍然可以访问数据。
重新措辞以合乎逻辑地回答您的问题:
1) 它是一个副本,并没有立即分配,因此被立即销毁。
2) 值存在于地址中,但地址被视为无效并可能导致内存问题(例如分段错误)
【讨论】:
【参考方案2】:原始代码不会被 GCC 编译,因为函数
E T::get()
return this->data;
返回值。当您在块中分配指针e2 = &(t.get());
时,它会生成data
的临时副本。从块中退出后,当您引用指针e2
时,它不再指向实际变量,因为临时变量超出了范围。所以为了纠正这个问题,你可以修改代码以返回对T
类的data
字段的引用:
E& T::get()
return data;
之后当你再次运行代码时,你会看到:
end of block
2
E is destroyed
E is destroyed
注意这两个E is destroyed
是因为有两个E
实例,一个用于e
,另一个用于t.data
,因为代码t.data = e;
会调用类的复制构造函数E
。因此最终有两个E
对象在程序退出时被破坏。
【讨论】:
以上是关于返回值会发生啥?的主要内容,如果未能解决你的问题,请参考以下文章