以下代码将指向类型为Entry
的对象的指针传递给函数modify_entry
,在函数体内部,unique_ptr
采用原始指针。但是,指针指向的对象似乎在函数返回后仍然存在。
当我编译这个代码
#include <iostream>
#include <memory>
struct Entry {
Entry(std::string name) : name_(name) { std::cout << "Constructor for " + name_ + 'n'; }
~Entry() { std::cout << "Destructor for " + name_ + 'n'; }
std::string name_;
};
void modify_entry(Entry* e_ptr){
std::cout << "Inside modify_entry()n";
std::unique_ptr<Entry>{e_ptr}->name_ += " Doe";
}
int main(int argc, const char * argv[])
{
Entry* entry_ptr = new Entry("John");
modify_entry(entry_ptr);
std::cout << "Back from modify_entry()n";
std::cout << entry_ptr->name_ << 'n'; // <---- line 25
return 0;
}
带有
clang版本3.4(标签/RERELEASE_34/final)
目标:x86_64-apple-darwin13.1.0
线程型号:posix
它运行时没有错误,输出为
John 的建造师
内部修改入口()
John Doe 的破坏者
从modify_entry()返回
John Doe
然而,在这里,由于第25行的原因,我得到了一个运行时错误。
Q:为什么运行clang生成的可执行文件时没有运行时错误?
如果有人能澄清情况,我将不胜感激。请注意,我并没有试图正确地转移所有权。这个错误代码的例子是调试过程的副产品。make_unique
和unique_ptr
的移动语义等都很棒,但这不是我想要的。
提前谢谢。
但是,指针指向的对象似乎在函数返回后仍然存在。
这里的关键是"似乎"。实际上,Entry
的寿命在这一行的末尾结束:
std::unique_ptr<Entry>{e_ptr}->name_ += " Doe";
unique_ptr
已经取得了内存的所有权,但临时unique_ptr
的生存期在该语句结束时结束,因此它也删除了它拥有的Entry
。访问对象以前使用的内存是未定义的行为。它在某些平台上有效,而在其他平台上无效,这正是未定义行为的本质。
Q: 为什么运行clang生成的可执行文件时没有运行时错误?
因为未定义的行为是未定义的。程序在对象的生存期结束后尝试访问该对象。C++没有为这样的程序定义任何行为。
make_unique
和unique_ptr
的移动语义等都很棒,像这样的东西只是你应该使用它们的另一个原因。
因为您正在执行的是未定义的行为,所以在main
函数中使用指向已销毁对象的指针时。
对象已被破坏(删除),但您仍然有一个指向所在位置的指针,取消引用该指针会导致未定义的行为。