unique_ptr的static_pointer_cast替代方案

时间:2016-03-20 22:28:29

标签: c++ c++11 casting shared-ptr unique-ptr

我了解将static_pointer_castunique_ptr一起使用会导致所包含数据的共享所有权。
换句话说,我想做的是:

unique_ptr<Base> foo = fooFactory();
// do something for a while
unique_ptr<Derived> bar = static_unique_pointer_cast<Derived>(foo);

无论如何,两个unique_ptr同时出现的结果不应该存在,所以它只是被禁止了。
是的,绝对是有道理的,这就是为什么不存在static_unique_pointer_cast之类的东西。

到目前为止,在我想存储指向这些基类的指针的情况下,我还需要将它们转换为某些派生类(例如,假设涉及类型擦除的场景),我使用了{{1因为我上面提到过的。

无论如何,我猜测shared_ptr是否有替代方案可以解决这个问题,或者在这种情况下它们是否真的是最佳解决方案。

3 个答案:

答案 0 :(得分:30)

原始指针

你的问题的解决方案是获取原始(非拥有)指针并强制转换它 - 然后让原始指针超出范围,让剩余的unique_ptr<Base>控制所拥有对象的生命周期。

像这样:

unique_ptr<Base> foo = fooFactory();

{
    Base* tempBase = foo.get();
    Derived* tempDerived = static_cast<Derived*>(tempBase);
} //tempBase and tempDerived go out of scope here, but foo remains -> no need to delete

Unique_pointer_cast

另一种选择是使用release()的{​​{1}}函数将其包装到另一个unique_ptr中。

喜欢这个

unique_ptr

请记住,这会使旧指针template<typename TO, typename FROM> unique_ptr<TO> static_unique_pointer_cast (unique_ptr<FROM>&& old){ return unique_ptr<TO>{static_cast<TO*>(old.release())}; //conversion: unique_ptr<FROM>->FROM*->TO*->unique_ptr<TO> } unique_ptr<Base> foo = fooFactory(); unique_ptr<Derived> foo2 = static_unique_pointer_cast<Derived>(std::move(foo));

无效

原始指针

的引用

为了完整答案,这个解决方案实际上是在评论中提出的OP对原始指针的一个小修改。

与使用原始指针类似,可以转换原始指针,然后通过derefering从中创建引用。在这种情况下,重要的是要保证创建的引用的生命周期不超过unique_ptr的生命周期。

样品:

foo

答案 1 :(得分:7)

  

据我所知,将static_pointer_cast与unique_ptr一起使用会导致所包含数据的共享所有权。

只有你定义得很糟糕。显而易见的解决方案是转移所有权,以便源对象最终为空。

如果您不想转让所有权,请使用原始指针。

或者,如果您想要两个所有者,请使用shared_ptr

看起来你的问题只是部分关于实际的转换操作,部分只是指针缺乏明确的所有权政策。如果您需要多个所有者,无论他们是使用相同的类型,还是将其转换为其他类型,那么您不应该使用unique_ptr

  

无论如何这样做的结果是两个unique_ptr不应该同时存在,所以它只是被禁止了。
  是的,绝对有道理,这就是为什么不存在像static_unique_pointer_cast这样的东西。

不,这不是它不存在的原因。它不存在,因为如果你需要它自己编写它是微不足道的(并且只要你给它独特所有权的理智语义)。只需将release()转换为指针,然后将其放入另一个unique_ptr。简单安全。

shared_ptr的情况并非如此,“显而易见”的解决方案没有做正确的事情:

shared_ptr<Derived> p2(static_cast<Derived*>(p1.get());

这会创建两个不同的shared_ptr对象,这些对象拥有相同的指针,但不共享所有权(即他们都会尝试删除它,导致未定义的行为)。

shared_ptr首次标准化时,没有安全的方法可以做到这一点,因此定义了static_pointer_cast和相关的强制转换函数。他们需要访问shared_ptr簿记信息的实施细节才能发挥作用。

然而,在C ++ 11标准化过程中,shared_ptr通过添加“别名构造函数”得到了增强,它允许您简单安全地进行转换:

shared_ptr<Derived> p2(p1, static_cast<Derived*>(p1.get());

如果此功能一直是shared_ptr的一部分,则可能(甚至可能)static_pointer_cast永远不会被定义。

答案 2 :(得分:0)

我想在Anedar的上一个回答中添加一些内容,该回答调用给定release()的{​​{1}}成员方法。如果想要实现std::unique_ptr< U >(除了dynamic_pointer_cast)以便将static_pointer_cast转换为std::unique_ptr< U >,还必须确保释放由唯一指针保护的资源在std::unique_ptr< T >失败的情况下正确运行(即返回dynamic_cast)。否则,会发生内存泄漏。

<强>代码

nullptr

输出(可能的排序)

#include <iostream>
#include <memory>

template< typename T, typename U >
inline std::unique_ptr< T > dynamic_pointer_cast(std::unique_ptr< U > &&ptr) {
    U * const stored_ptr = ptr.release();
    T * const converted_stored_ptr = dynamic_cast< T * >(stored_ptr);
    if (converted_stored_ptr) {
        std::cout << "Cast did succeeded\n";
        return std::unique_ptr< T >(converted_stored_ptr);
    }
    else {
        std::cout << "Cast did not succeeded\n";
        ptr.reset(stored_ptr);
        return std::unique_ptr< T >();
    }
}

struct A { 
    virtual ~A() = default;
};
struct B : A {
    virtual ~B() { 
        std::cout << "B::~B\n"; 
    }
};
struct C : A {
    virtual ~C() { 
        std::cout << "C::~C\n"; 
    }
};
struct D { 
    virtual ~D() { 
        std::cout << "D::~D\n"; 
    }
};

int main() {

  std::unique_ptr< A > b(new B);
  std::unique_ptr< A > c(new C);
  std::unique_ptr< D > d(new D);

  std::unique_ptr< B > b1 = dynamic_pointer_cast< B, A >(std::move(b));
  std::unique_ptr< B > b2 = dynamic_pointer_cast< B, A >(std::move(c));
  std::unique_ptr< B > b3 = dynamic_pointer_cast< B, D >(std::move(d));
}

Cast did succeeded Cast did not succeeded Cast did not succeeded B::~B D::~D C::~C C的析构函数如果使用,则不会被调用:

D