为什么这个悬空 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
}
在下面的代码中,我在作用域中创建了一个 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
}