我了解将static_pointer_cast
与unique_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
是否有替代方案可以解决这个问题,或者在这种情况下它们是否真的是最佳解决方案。
答案 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
另一种选择是使用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