Unique_ptr:被释放的指针在放入列表时未分配
Posted
技术标签:
【中文标题】Unique_ptr:被释放的指针在放入列表时未分配【英文标题】:Unique_ptr: pointer being freed was not allocated when emplaced in list 【发布时间】:2018-08-01 15:34:01 【问题描述】:我有一个像这样的简单 C++ 程序:
#include <iostream>
#include <list>
#include <memory>
int main(void)
std::list<std::unique_ptr<int>> l;
int x = 5;
// throws runtime error: pointer being freed was not allocated
l.emplace_back(&x);
当程序运行时,我得到以下输出:
a.out(59842,0x7fffb13d1380) malloc: *** error for object 0x7ffee81489ac: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
[1] 59842 abort ./a.out
这告诉我,在销毁列表期间,已创建的 unique_ptr 对象被销毁,而在销毁期间,指向 x
的指针被释放,而它从未被 malloc
ed 释放。
但是我希望在unique_ptr
超出范围并且是destroyed
的情况下也会发生同样的情况:
#include <iostream>
#include <list>
#include <memory>
int main(void)
int x = 5;
// does not throw runtime error when falling out of scope and being destructed
std::unique_ptr<int> p(&x);
但是,当上面的unique_ptr p
超出范围并被销毁时,程序运行良好。为什么第一个程序在销毁unique_ptr
时给我一个错误,而第二个程序却没有?为什么在列表中放置 unique_ptr
会导致其破坏失败?
【问题讨论】:
两者都是 UB,因此行为可能不同。 【参考方案1】:未定义的行为可能导致任何事情,从崩溃到明显成功的运行,再到英国投票退出欧盟(哎呀,对不起,伙计们……空指针取消引用可以做的事情真是太神奇了)。
编译器是极其复杂的东西。您的程序不是将由目标计算机按顺序执行的指令的一对一映射;它大致描述了您希望计算机如何运行。两者之间的转换非常深奥和复杂。当你走下坡路时,你会以这样一种方式破坏语言:编译器的内部工作——不断做出假设并采取它知道可以做的捷径,尤其是在应用所谓的“优化”时——被打破了不可预知的方式。
这是其中之一。
简而言之,如果您期望 UB 做任何特别的事情,那么您已经失败了。
通过分析编译器的源代码和生成的程序集,可以准确确定您的 UB 在这种情况下的行为方式。但这将是一个漫长、艰巨且最终完全没有意义的过程。
只是不要从你没有动态分配的东西中创建智能指针(或者,如果你出于某种原因真的想要,提供一个自定义删除器,它不会尝试 delete
不是 @ 的东西987654322@d)。简单!
【讨论】:
【参考方案2】:我认为发生的事情是第二个示例中的变量 p
刚刚被优化掉,因此不会导致运行时错误。
但是发生的错误很明显,它正在尝试释放在堆栈上分配的内存。请注意,智能指针通常与堆分配的内存一起使用,而不是在堆栈上分配的内存。
【讨论】:
您可能可以使用无操作删除器使其与堆栈分配的对象一起正常工作......只是出于好奇。 你是对的。使用 -O0 运行会产生错误。然而,尽管我认为 volatile 限定符会阻止优化,但使用带有 -O3 的volatile std::unique_ptr<int> p(&x);
运行仍然不会产生错误。
@RaeesRajwani Making p
volatile
在这里没有什么有趣的事情。
@RaeesRajwani 这是对volatile
所做工作的过度简化,并且可能是您的整个程序被优化了,因为它什么都不做。 (在列表的情况下,这对您的编译器来说更难证明,所以也许它不会打扰。)
@RaeesRajwani 确实如此,但它仍然没有改变他的代码不必崩溃的事实。未定义的行为意味着不需要发生错误。【参考方案3】:
我已经复制并编译了你的代码:
$ g++ omg.cpp && ./a.exe
Aborted (core dumped)
您可以尝试添加另一个级别的间接:
int main(void)
int x = 5;
std::unique_ptr<int> p(&x);
看看有没有帮助。
【讨论】:
当然它和我的 5.3 一样...当你运行二进制文件时会发生什么? 我没有运行,因为godbolt 只允许编译代码但不允许运行它。但即使我运行它,它也会抛出同样的错误,因为释放分配在堆栈上的内存只是一个禁忌。 这不是间接级别。 @ProXicT 不,您不能在这里依赖错误/诊断(如问题所示)。 @LightnessRacesinOrbit 是的,你是对的。我的意思是释放堆栈内存可能会导致任何结果。以上是关于Unique_ptr:被释放的指针在放入列表时未分配的主要内容,如果未能解决你的问题,请参考以下文章
如何在构造函数中使用删除器初始化 std::unique_ptr?
实战c++中的智能指针unique_ptr系列-- unique_ptr的get()赋给普通指针后的崩溃(其实是生命周期惹的祸)