我有一个像这样的简单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
的指针。
但是我希望在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
会导致其销毁失败?
答案 0 :(得分:3)
未定义的行为可能导致任何事情,从崩溃到表面上看似成功的运行,再到英国投票退出欧盟(哎呀,对此感到抱歉,伙计们……空指针取消引用可以做什么令人惊讶)。
编译器是极其复杂的东西。您的程序不是目标计算机将按顺序执行的指令的一对一映射。它大致描述了您希望计算机如何运行。两者之间的转换是深奥而复杂的。当您进行滑雪训练时,您会以一种破坏编译器内部工作的方式来破坏语言,编译器的内部工作会不断地做出假设,并采取允许其执行的捷径,特别是在应用所谓的“优化”时尤其如此。不可预测的方式。
这是其中之一。
简而言之,如果您期望UB做一些特别的事情,那么您已经失败了。
通过分析编译器的源代码和从构建中产生的程序集,有可能确切确定UB在这种情况下的行为表现。但这将是一个漫长,艰巨且最终完全没有意义的过程。
请不要从没有动态分配的内容中创建智能指针(或者,如果出于某些原因,确实要提供自定义的删除器,而该删除器将不会尝试delete
那些不是' t new
d)。简单!
答案 1 :(得分:1)
我认为发生的事情是第二个示例中的变量p
刚刚被优化,因此不会导致运行时错误。
但是发生的错误很明显,它试图释放在堆栈上分配的内存。请注意,智能指针通常与堆分配的内存结合使用,而不是与在堆栈上分配的内存结合使用。
答案 2 :(得分:-3)
我已经复制了您的代码并对其进行了编译:
$ g++ omg.cpp && ./a.exe
Aborted (core dumped)
您可以尝试添加另一个间接级别:
int main(void) {
int x = 5;
{
std::unique_ptr<int> p(&x);
}
}
看看是否有帮助。