为什么这个悬空 std::weak_ptr 不会导致 SEGFAULT?

Why doesn't this dangling std::weak_ptr cause SEGFAULT?

在下面的代码中,我在作用域中创建了一个 shared_ptr,并将其分配给了一个 weak_ptr。为什么 运行 我没有得到 SEGFAULT 的代码,因为 wp 应该在范围之外无效,对吧?

namespace {
    struct Dummy {
        int x;
        void foo() {
            std::cout << "dummy created\n";
        }
        ~Dummy()
        {
            std::cout << "dummy destroyed\n";
        }
    };
}
TEST(experimental, ptr_test){
    std::weak_ptr<Dummy> wp;
    {
        auto sp = std::make_shared<Dummy>();
        wp = sp;
    }
    wp.lock()->foo();
};

一般来说,你不会得到段错误,除非你真的对无效内存做了一些事情(然后它不会总是发生段错误 - 由硬件向 OS 发送信号,然后到 OS 使程序真正崩溃)。如果您要在 foo 中设置 x,您可能更有可能看到段错误 - 但正如 user2357112 指出的那样,C++ 标准不保证无效代码的段错误。

尽管如此,您实际上并没有取消引用那里的任何内容。如果锁定的 shared_ptr 为空,锁定方法仍将 return 为 shared_ptr,但 shared_ptr 也将为空。在此示例中,foo 不会在我的编译器上崩溃,因为它从不取消引用空指针,但它是未定义的行为,因此您永远不知道会发生什么。但是,bar 总是会崩溃,因为它需要取消引用指针才能到达 x。

这恰好起作用的原因是所有成员函数都编译为普通函数,这些函数将指向对象的指针作为它们的第一个参数,可以从函数体访问,如 this。如果函数体中没有任何内容取消引用 this,则在 nullptr 上调用此函数可能在大多数情况下都有效。但是你不应该这样做,未来的编译器更改或移植到另一个体系结构可能会导致崩溃。

#include <iostream>
#include <memory>

struct Dummy {
   int x;
   Dummy()
      : x(10) {
      std::cout << "Dummy created" << std::endl;
   }

   ~Dummy() {
      std::cout << "Dummy destroyed" << std::endl;
   }

   void foo() {
      std::cout << "foo" << std::endl;
   }

   void bar() {
      std::cout << x << std::endl;
   }
};

int main() {
   std::weak_ptr<Dummy> wp;
   {
      auto sp = std::make_shared<Dummy>();
      wp = sp;
   }

   auto locked = wp.lock();
   if(locked.get() == nullptr) {
      std::cout << "Locked pointer is null" << std::endl;
   }

   locked->foo();             // Does not crash
   ((Dummy*)nullptr)->foo();  // Does not crash
   locked->bar();             // Will crash
}