static_cast shared_ptr会导致未定义的行为

static_cast on shared_ptr leads to undefined behaviour

本文关键字:未定义 cast shared ptr static      更新时间:2023-10-16

根据这本书:The C++ Standard Library: a tutorial and reference我引用以下内容:

强制转换运算符允许将指针转换为不同类型的指针。语义与相应的运算符相同,结果是另一个不同类型的共享指针。请注意,无法使用普通强制转换运算符,因为它会导致未定义的行为

因此,例如,以下代码会产生未定义的行为:

shared_ptr<Base> base_ptr (new Derived);
shared_ptr<Derived>(static_cast<Derived*>(base_ptr.get())); --> Undefined Behaviour.

因此,应改用static_pointer_cast()

第二个例子:

shared_ptr<void> sp(new int);                // shared pointer holds a void* internally
shared_ptr<int>(static_cast<int*>(sp.get())) // ERROR: undefined behavior

这样的主张确实得到标准的支持吗?

给定:

shared_ptr<Base> base_ptr (new Derived);  // ** see below
shared_ptr<Derived> derived_ptr;

表达式:

Derived* p = static_cast<Derived *>(base_ptr.get());

从基类指针正确生成指向派生类接口的指针。但是,这是一个原始指针。

这本身不是UB。

但是,下一个逻辑错误可能是:

derived_ptr.reset(p);

这将创建一个微妙而令人讨厌的错误,因为您现在有两个不同的shared_ptr,每个都有自己的控制块(它跟踪受控对象的生命周期的方法(。

derived_ptrbase_ptr现在将独立控制对象指针的生存期p

相比之下,

derived_ptr = std::static_pointer_cast<Derived>(base_ptr);

导致derived_ptrbase_ptr共享相同的控制块,因此,新对象的生存期将得到正确管理。

请注意,在所述示例中,在任何情况下您都有 UB。

您只能static_castDerived是否真的指向base_ptr指向Derived。在你的情况下,它不是。它指向一个Base,再多的选角也无法正确地将其压倒。

我已经进行了编辑以纠正这一点,如上。

不要在共享指针上使用static_cast。这是错误的,因为您还必须传输删除程序信息。使用std::static_pointer_cast

std::shared_ptr<int> int_ptr = std::static_pointer_cast<int>(sp);

您正在执行的操作将使用新的引用计数器创建一个新的shared_ptr对象。当两个shared_ptr对象都超出范围时,两个对象都将尝试删除相同的指针,这将导致段错误。

共享指针是两件事; 指向数据的指针和指向控制块的指针。

将指向数据的指针显式转换为共享 ptr 时,会导致它分配新的控制块。

如果同一个 poimter 由 2 个控制块控制,则当两个删除器超出范围时,将调用它们。 默认删除程序删除指针,并且执行两次未定义的行为。

但是没有立即出错。

在这里,我们有一种不使用静态指针转换的替代方法:

shared_ptr<void> sp(new int);                // shared pointer holds a void* internally
shared_ptr<int>(sp, static_cast<int*>(sp.get())) //OK: aliasing constructor

shared_ptr的"别名"构造函数允许您传递单独的控制块和数据指针。 控制块由任何类型的另一个共享 ptr(从中添加引用并获取控制块(表示。