std::unique_ptr超出范围后仍然可以访问的对象.不同的运行时行为

Object still accessible after std::unique_ptr goes out of scope. Differing runtime behaviours

本文关键字:对象 访问 运行时 ptr unique 范围 std      更新时间:2023-10-16

以下代码将指向类型为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_uniqueunique_ptr的移动语义等都很棒,但这不是我想要的。

提前谢谢。

但是,指针指向的对象似乎在函数返回后仍然存在。

这里的关键是"似乎"。实际上,Entry的寿命在这一行的末尾结束:

 std::unique_ptr<Entry>{e_ptr}->name_ += " Doe";

unique_ptr已经取得了内存的所有权,但临时unique_ptr的生存期在该语句结束时结束,因此它也删除了它拥有的Entry。访问对象以前使用的内存是未定义的行为。它在某些平台上有效,而在其他平台上无效,这正是未定义行为的本质。

Q: 为什么运行clang生成的可执行文件时没有运行时错误?

因为未定义的行为是未定义的。程序在对象的生存期结束后尝试访问该对象。C++没有为这样的程序定义任何行为。

make_uniqueunique_ptr的移动语义等都很棒,像这样的东西只是你应该使用它们的另一个原因。

因为您正在执行的是未定义的行为,所以在main函数中使用指向已销毁对象的指针时。

对象已被破坏(删除),但您仍然有一个指向所在位置的指针,取消引用该指针会导致未定义的行为。