如何在将原始指针移动到基类构造函数之前从unique_ptr中提取原始指针
How can I extract a raw pointer from a unique_ptr before moving it into my base class constructor
我有一个非常具体的需求来访问特定于派生类的功能,我在构造包含类时unique_ptr
获得了派生类的实例。然后,此包含类必须将unique_ptr
向上转换为其基类,移动到包含类的基类构造函数,最终在该包含基类中move
d 以获得所有权。一些代码应该会有所帮助:
class MemberBase {};
class MemberDerived : public MemberBase { /*some public stuff not in MemberBase*/ };
class MainBase {
std::unique_ptr<MemberBase> member_;
public:
MainBase(std::unique_ptr<MemberBase> member) : member_(std::move(member)) {}
};
class MainDerived : public MainBase {
MemberDerived* member_derived_;
// This class proceeds to use MemberDerived-only functions
public:
// How to write the initialization list below?
MainDerived(std::unique_ptr<MemberDerived> member)
: MainBase(std::move(member)), member_derived_(member.get() /*nullptr!!*/) {}
};
忽略整体类设计的问题(我知道与MainDerived
中特定于MemberDerived
函数的耦合并不理想;这是继承的代码,如果不进行重大重构就无法更改(,如何在将unique_ptr
转发到MainBase
之前抓住原始指针?
在下面找到一些我想到的想法,以及为什么我认为它们不是很好。
- 向下投射保护的访问器:
// Add this method to the protected section of MainBase:
MemberBase* MainBase::get_member() { return member_.get(); }
// Then downcast in MainDerived's c'tor
MainDerived::MainDerived(std::unique_ptr<MemberDerived> member)
: MainBase(std::move(member)), member_derived_(dynamic_cast<MemberDerived*>(get_member())) {}
这应该有效,但使用dynamic_cast
(本身就是主要缺点(,当然,如果有人将传递给 c'tor 的类型更改为不是派生自MemberDerived
的类型,它将在没有编译器帮助的情况下中断。
- 在
- 指针中传递两次:
// member and member_derived must point to the same object!
MainDerived::MainDerived(std::unique_ptr<MemberDerived> member, MemberDerived* member_derived)
: MainBase(std::move(member)), member_derived_(member_derived) {}
除了制作一个非常丑陋的 c'tor 签名之外,用户很容易为两个参数传递不同的指针或在调用get
之前执行move
。此外,用户现在被迫创建一个局部变量以将其传递到两个地方。我有没有提到它很丑?
- 使用辅助函数技巧翻转初始化顺序:
template <typename T>
std::unique_ptr<T> ExtractPointer(std::unique_ptr<T> p, T** target) {
*target = p.get();
return std::move(p);
}
MainDerived::MainDerived(std::unique_ptr<MemberDerived> member)
: MainBase(ExtractPointer(std::move(member), &member_derived_)) {}
现在我实际上有点惊讶这没有产生任何警告/错误(带有-Wall
的 gcc 5.4.0 (。我的一部分喜欢这个,因为它似乎很安全,因为它很难打破,但member_derived_
的迂回初始化让我有点不寒而栗。
您的第二个解决方案已经完成了一半。请注意,构造函数不需要public
。您可以将 2 参数设为一个private
,并编写委托给它的 1 参数public
构造函数。
class MainDerived : public MainBase {
MemberDerived* member_derived_;
MainDerived(std::unique_ptr<MemberDerived> &member, MemberDerived *member_derived)
: MainBase(std::move(member)), member_derived_(member_derived) { }
public:
MainDerived(std::unique_ptr<MemberDerived> member)
: MainDerived(member, member.get()) { }
};
member
必须在private
构造函数中通过引用来获取,因为未指定函数调用参数的计算顺序。因此,您无法安全地将member
从public
构造函数移动到private
构造函数的参数中,因为这样移动和get()
的顺序将未指定,但我们需要在移动之前get()
发生。最简单的解决方法是让private
构造函数通过引用获取member
,这不会改变它。(您也可以使用{}
而不是()
来强制实施计算顺序,但让您的代码依赖于这种微妙的结构并不是一个好主意。
既然你知道指针指向MemberDerived
,你就不需要dynamic_cast
。一个static_cast
就可以了。
如果您担心在更改某些内容时在类型中出错,那么只需参考元素类型:
MainDerived(std::unique_ptr<MemberDerived> member)
: MainBase(std::move(member))
, member_derived_(static_cast<decltype(member)::element_type*>(member_.get()))
{}
正如你提到的,这需要member_
protected
或protected
才能存在。
据我所知,这不会导致未定义的行为,除非有人传递了一个实际上并不指向与其element_type
相同或派生类型的对象的std::unique_ptr
,在这种情况下,使用此std::unique_ptr
本身是不安全的,因为如果实例在没有事先移动的情况下被销毁,它会导致未定义的行为。
至于你的 3. 建议的方法:
我认为这在 C++17 中在技术上是允许的,因为member_derived_
具有空的初始化,因此它的生命周期从分配存储时开始,而不是在其初始化完成时开始。
但是当前的 C++20 草案删除了这个例外,因此member_derived_
的生命周期将仅在其空洞初始化后开始,这使得它使用未定义的行为。
- 是否可以将llvm::FunctionType转换为C/C++原始函数指针
- 从堆栈分配的原始指针构造智能指针
- 将unique_ptr分配给原始指针
- 如何将唯一指针的 std::vector 转换为原始指针的 std::span?
- <Base> <Derived> 具有相同原始指针共享引用的 shared_ptr 和 shared_ptr 实例是否计数?
- C++原始指针和"delete"
- 为什么 C++ 地址中的矢量无法通过原始指针访问
- 复制后删除原始数组指针将前 3 个字节设置为 0
- 如何正确实现具有原始指针的类的复制构造函数?
- 如何在将原始指针移动到基类构造函数之前从unique_ptr中提取原始指针
- C++指针无法更改其原始值
- 为包含原始指针的对象C++智能指针
- unique_ptr<T>::get() 返回的指针在原始unique_ptr被销毁后不是 nullptr
- c++:复制、删除和运算符=在原始指针映射中
- C++模板,用于通过常量引用和原始指针传递向量
- C++为什么原始指针不会增加shared_ptr的引用计数?
- 从shared_ptr获取原始指针以将其传递给需要 raw 的函数
- 如何包装多级原始指针以赋予其容器语义
- 为什么我可以通过原始指针而不是shared_ptr来修改对象
- Qt - 将空指针(原始数据)转换为 QImage 并将其显示在标签上