C++ unique_ptr 的 static_pointer_cast 的替代方案
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/36120424/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Alternatives of static_pointer_cast for unique_ptr
提问by skypHyman
I understand that using static_pointer_cast
with unique_ptr
would lead to a shared ownership of the contained data.
In other terms, what I'd like to do is:
我知道使用static_pointer_cast
withunique_ptr
会导致所包含数据的共享所有权。
换句话说,我想做的是:
unique_ptr<Base> foo = fooFactory();
// do something for a while
unique_ptr<Derived> bar = static_unique_pointer_cast<Derived>(foo);
Anyway doing that results with two unique_ptr
that should never exist at the same time, so it is simply forbidden.
Right, it makes sense, absolutely, that's why there doesn't exist anything like static_unique_pointer_cast
indeed.
无论如何,这样做会导致两个unique_ptr
永远不会同时存在的结果,所以这是完全禁止的。
是的,这是有道理的,绝对的,这就是为什么不存在任何类似的东西static_unique_pointer_cast
。
So far, in cases where I want to store pointers to those base classes, but I also need to cast them to some derived classes (as an example, imagine a scenario involving type erasure), I've used shared_ptr
s because of what I've above mentioned.
到目前为止,如果我想存储指向这些基类的指针,但我还需要将它们强制转换为某些派生类(例如,想象一个涉及类型擦除的场景),我使用shared_ptr
s 是因为我'上面提到过。
Anyway, I was guessing if there are alternatives to shared_ptr
s for such a problem or if they are really the best solution in that case.
无论如何,我在猜测shared_ptr
对于这样的问题是否有s 的替代方案,或者它们是否真的是这种情况下的最佳解决方案。
回答by Anedar
Raw pointers
原始指针
The solution for your problem is to get the raw (non-owning) pointer and cast it - then just let the raw pointer go out of scope and let the remaining unique_ptr<Base>
constrol the lifetime of the owned object.
您的问题的解决方案是获取原始(非拥有)指针并将其unique_ptr<Base>
强制转换- 然后让原始指针超出范围并让剩余的控制拥有对象的生命周期。
Like this:
像这样:
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
unique_pointer_cast
The other option is to use the release()
function of unique_ptr
to wrap it into another unique_ptr.
另一种选择是使用 的release()
函数将unique_ptr
其包装到另一个 unique_ptr 中。
Like this
像这样
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));
Remember that this invalidates the old pointer foo
请记住,这会使旧指针无效 foo
Reference from raw pointers
来自原始指针的引用
Just for completeness of the answer, this solution was actually proposed as a small modification of the raw pointers by the OP in the comments.
只是为了答案的完整性,这个解决方案实际上是作为 OP 在评论中对原始指针的一个小修改而提出的。
Similar to using raw pointers one can cast the raw pointers and then create a reference out of them by derefering. In this case it is important to guarantee that the lifetime of the created reference does not exceed the lifetime of the unique_ptr.
与使用原始指针类似,可以转换原始指针,然后通过取消引用从它们中创建一个引用。在这种情况下,确保创建的引用的生命周期不超过 unique_ptr 的生命周期很重要。
Sample:
样本:
unique_ptr<Base> foo = fooFactory();
Derived& bar = *(static_cast<Derived*>(foo.get()));
//do not use bar after foo goes out of scope
回答by Jonathan Wakely
I understand that using static_pointer_cast with unique_ptr would lead to a shared ownership of the contained data.
我知道将 static_pointer_cast 与 unique_ptr 一起使用会导致所包含数据的共享所有权。
Only if you define it badly. The obvious solution would be for it to transfer ownership, so that the source object ends up empty.
只有当你定义不好时。显而易见的解决方案是让它转移所有权,这样源对象最终会变成空的。
If you don't want to transfer ownership then just use a raw pointer.
如果您不想转移所有权,则只需使用原始指针。
Or if you want two owners then use shared_ptr
.
或者,如果您想要两个所有者,请使用shared_ptr
.
It seems like your question is only partly about the actual cast operation, and partly just lack of a clear ownership policy for the pointer. If you need multiple owners, whether they both use the same type, or whether one is cast to a different type, then you should not be using unique_ptr
.
似乎您的问题仅部分与实际的转换操作有关,部分只是缺乏明确的指针所有权政策。如果您需要多个所有者,无论他们都使用相同的类型,还是将一个转换为不同的类型,那么您不应该使用unique_ptr
.
Anyway doing that results with two unique_ptr that should never exist at the same time, so it is simply forbidden.
Right, it makes sense, absolutely, that's why there doesn't exist anything like static_unique_pointer_cast indeed.
无论如何,这样做会导致两个不应该同时存在的 unique_ptr ,所以它只是被禁止的。
是的,这是有道理的,绝对的,这就是为什么确实不存在像 static_unique_pointer_cast 这样的东西。
No, that's not why it doesn't exist. It doesn't exist because it's trivial to write it yourself, if you need it (and as long as you give it sane semantics of unique ownership). Just get the pointer out with release()
cast it, and put it in another unique_ptr
. Simple and safe.
不,这不是它不存在的原因。它不存在,因为如果你需要它,你自己编写它是微不足道的(并且只要你给它独特所有权的理智语义)。只需使用release()
cast取出指针,然后将其放入另一个unique_ptr
. 简单又安全。
That isn't the case for the shared_ptr
, where the "obvious" solution doesn't do the right thing:
情况并非如此shared_ptr
,其中“明显”的解决方案没有做正确的事情:
shared_ptr<Derived> p2(static_cast<Derived*>(p1.get());
That would create two different shared_ptr
objects that own the same pointer, but don't share ownership (i.e. they would both try to delete it, causing undefined behaviour).
这将创建两个shared_ptr
拥有相同指针的不同对象,但不共享所有权(即它们都会尝试删除它,导致未定义的行为)。
When shared_ptr
was first standardized there was no safe way to do that, so static_pointer_cast
and the related casting functions were defined. They needed access to the implementation details of the shared_ptr
bookkeeping info to work.
当shared_ptr
第一次标准化时,没有安全的方法来做到这一点,因此static_pointer_cast
定义了相关的转换函数。他们需要访问shared_ptr
簿记信息的实施细节才能工作。
However, during the C++11 standardization process shared_ptr
was enhanced by the addition of the "aliasing constructor" which allows you to do the cast simply and safely:
但是,在 C++11 标准化过程中shared_ptr
,通过添加“别名构造函数”得到了增强,它允许您简单安全地进行转换:
shared_ptr<Derived> p2(p1, static_cast<Derived*>(p1.get());
If this feature had always been part of shared_ptr
then it's possibly, maybe even likely, that static_pointer_cast
would never have been defined.
如果此功能一直是其中的一部分,shared_ptr
那么它可能static_pointer_cast
永远不会被定义。
回答by Matthias
I would like to add something to the previous answer of Anedarwhich calls the release()
member method of the given std::unique_ptr< U >
. If one wants to implement a dynamic_pointer_cast
as well (in addition to a static_pointer_cast
) for converting std::unique_ptr< U >
to std::unique_ptr< T >
, one must ensure the resource guarded by the unique pointer is released properly in case the dynamic_cast
fails (i.e. returns a nullptr
). Otherwise, a memory leak occurs.
我想在Anedar的先前答案中添加一些内容,该答案调用release()
给定std::unique_ptr< U >
. 如果还想实现 a dynamic_pointer_cast
(除了 a static_pointer_cast
)以转换std::unique_ptr< U >
为std::unique_ptr< T >
,则必须确保在dynamic_cast
失败(即返回 a nullptr
)的情况下正确释放由唯一指针保护的资源。否则,会发生内存泄漏。
Code:
代码:
#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));
}
Output (possible ordering):
输出(可能的排序):
Cast did succeeded
Cast did not succeeded
Cast did not succeeded
B::~B
D::~D
C::~C
The destructors of C
and D
will not be called, if one uses:
的析构函数C
,并D
不会被调用,如果一个用途:
template< typename T, typename U >
inline std::unique_ptr< T > dynamic_pointer_cast(std::unique_ptr< U > &&ptr) {
return std::unique_ptr< T >(dynamic_cast< T * >(ptr.release()));
}