shared_ptr上的static_cast导致未定义的行为

时间:2017-07-29 18:45:09

标签: c++

根据这本书: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

标准确实支持这样的声明吗?

3 个答案:

答案 0 :(得分:3)

假设:

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_cast 确实指向Derived,则您只能base_ptrDerived。在你的情况下,它不是。它指向Base并且没有任何数量的投射会正确地向下投射。

我已在上面进行了编辑以纠正此问题。

答案 1 :(得分:1)

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

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

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

答案 2 :(得分:1)

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

当您将指向数据的指针显式转换为共享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

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