static_pointer_cast 的替代 unique_ptr
Alternatives of static_pointer_cast for unique_ptr
我知道将 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
这样的东西的原因。
到目前为止,如果我想存储指向那些基 类 的指针,但我还需要将它们转换为一些派生的 类(例如,想象一个涉及类型的场景擦除),我使用 shared_ptr
s 因为我上面提到的。
无论如何,我在猜测是否有 shared_ptr
s 的替代方案来解决这样的问题,或者它们是否真的是那种情况下的最佳解决方案。
#原始指针
你的问题的解决方案是获取原始(非拥有)指针并将其转换 - 然后让原始指针超出范围并让剩余的 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
另一种选择是使用 unique_ptr
的 release()
函数将其包装到另一个 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));
记住这会使旧指针失效foo
#来自原始指针的引用
为了回答的完整性,这个解决方案实际上是 OP 在评论中提出的对原始指针的一个小修改。
类似于使用原始指针,您可以强制转换原始指针,然后通过取消引用从中创建一个引用。在这种情况下,重要的是要保证创建的引用的生命周期不超过 unique_ptr.
的生命周期
样本:
unique_ptr<Base> foo = fooFactory();
Derived& bar = *(static_cast<Derived*>(foo.get()));
// do not use bar after foo goes out of scope
I understand that using static_pointer_cast with unique_ptr would lead to a shared ownership of the contained data.
除非你定义不好。显而易见的解决方案是转移所有权,以便源对象最终为空。
如果您不想转移所有权,那么只需使用原始指针。
或者,如果您想要两个所有者,则使用 shared_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.
不,这不是它不存在的原因。它不存在,因为如果您需要它,您自己编写它是微不足道的(只要您赋予它唯一所有权的理智语义)。只需用 release()
将指针取出,然后将其放入另一个 unique_ptr
。简单安全。
shared_ptr
的情况并非如此,其中 "obvious" 解决方案没有做正确的事情:
shared_ptr<Derived> p2(static_cast<Derived*>(p1.get());
这将创建两个不同的 shared_ptr
对象,它们拥有相同的指针,但不共享所有权(即它们都会尝试删除它,从而导致未定义的行为)。
当 shared_ptr
首次标准化时,没有安全的方法可以做到这一点,因此定义了 static_pointer_cast
和相关的转换函数。他们需要访问 shared_ptr
簿记信息的实施细节才能工作。
然而,在 C++11 标准化过程中,shared_ptr
通过添加 "aliasing constructor" 得到了增强,它允许您简单而安全地进行转换:
shared_ptr<Derived> p2(p1, static_cast<Derived*>(p1.get());
如果此功能一直是 shared_ptr
的一部分,那么 static_pointer_cast
可能永远不会被定义。
我想对 的先前答案添加一些内容,它调用给定 std::unique_ptr< U >
的 release()
成员方法。如果要实现 dynamic_pointer_cast
以及(除了 static_pointer_cast
)以将 std::unique_ptr< U >
转换为 std::unique_ptr< T >
,则必须确保释放由唯一指针保护的资源以防 dynamic_cast
失败(即 returns a 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
的析构函数不会被调用,如果使用:
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()));
}
我知道将 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
这样的东西的原因。
到目前为止,如果我想存储指向那些基 类 的指针,但我还需要将它们转换为一些派生的 类(例如,想象一个涉及类型的场景擦除),我使用 shared_ptr
s 因为我上面提到的。
无论如何,我在猜测是否有 shared_ptr
s 的替代方案来解决这样的问题,或者它们是否真的是那种情况下的最佳解决方案。
#原始指针
你的问题的解决方案是获取原始(非拥有)指针并将其转换 - 然后让原始指针超出范围并让剩余的 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
另一种选择是使用 unique_ptr
的 release()
函数将其包装到另一个 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));
记住这会使旧指针失效foo
#来自原始指针的引用 为了回答的完整性,这个解决方案实际上是 OP 在评论中提出的对原始指针的一个小修改。
类似于使用原始指针,您可以强制转换原始指针,然后通过取消引用从中创建一个引用。在这种情况下,重要的是要保证创建的引用的生命周期不超过 unique_ptr.
的生命周期样本:
unique_ptr<Base> foo = fooFactory();
Derived& bar = *(static_cast<Derived*>(foo.get()));
// do not use bar after foo goes out of scope
I understand that using static_pointer_cast with unique_ptr would lead to a shared ownership of the contained data.
除非你定义不好。显而易见的解决方案是转移所有权,以便源对象最终为空。
如果您不想转移所有权,那么只需使用原始指针。
或者,如果您想要两个所有者,则使用 shared_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.
不,这不是它不存在的原因。它不存在,因为如果您需要它,您自己编写它是微不足道的(只要您赋予它唯一所有权的理智语义)。只需用 release()
将指针取出,然后将其放入另一个 unique_ptr
。简单安全。
shared_ptr
的情况并非如此,其中 "obvious" 解决方案没有做正确的事情:
shared_ptr<Derived> p2(static_cast<Derived*>(p1.get());
这将创建两个不同的 shared_ptr
对象,它们拥有相同的指针,但不共享所有权(即它们都会尝试删除它,从而导致未定义的行为)。
当 shared_ptr
首次标准化时,没有安全的方法可以做到这一点,因此定义了 static_pointer_cast
和相关的转换函数。他们需要访问 shared_ptr
簿记信息的实施细节才能工作。
然而,在 C++11 标准化过程中,shared_ptr
通过添加 "aliasing constructor" 得到了增强,它允许您简单而安全地进行转换:
shared_ptr<Derived> p2(p1, static_cast<Derived*>(p1.get());
如果此功能一直是 shared_ptr
的一部分,那么 static_pointer_cast
可能永远不会被定义。
我想对 std::unique_ptr< U >
的 release()
成员方法。如果要实现 dynamic_pointer_cast
以及(除了 static_pointer_cast
)以将 std::unique_ptr< U >
转换为 std::unique_ptr< T >
,则必须确保释放由唯一指针保护的资源以防 dynamic_cast
失败(即 returns a 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
的析构函数不会被调用,如果使用:
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()));
}