c++ - C+ + - 在将它移入基类构造函数之前,如何从unique_ptr中提取原始指针

我需要访问特定于派生类的功能,我在unique_ptr的结构中得到了一个实例,然后,此包含类必须将包含unique_ptr向上转换到它基类,其中move一些代码应该有帮助:


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!!*/) {}


};



在下面找到一些我曾经想到的点子,以及为什么我认为它们不太好。

  • 向下转换protected访问器:
  • 
    // 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) {}
    
    
    
    

除了使move相当丑陋之外,在用户参数传递或传递不同的指针之前调用,此外,用户现在被迫创建一个局部变量来将它传递到两个地方,

  • 翻转带有helper函数技巧的初始化顺序:
  • 
    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_的circuit回初始化给我带来了些许寒意。

时间: 作者:

unique_ptr有一个release()方法,它允许你中断所引用的对象unique_ptr之间的,然后可以委托给另一个构造函数,将释放的指针传递给它,并让原始的unique_ptr,新的构造函数可以重建unique_ptr并初始化成员。


class MainDerived : public MainBase {


 MemberDerived* member_derived_;



 MainDerived(MemberDerived *member)


 : MainBase(std::unique_ptr<MemberBase>(member)), member_derived_(member) { }



 public:


 MainDerived(std::unique_ptr<MemberDerived> member)


 : MainDerived(member.release()) { }


};



作者:

由于你知道指针指向MemberDerived,因此你不需要dynamic_caststatic_cast就可以了。

如果你担心在类型中出错时有更改,那么只需引用元素类型:


MainDerived(std::unique_ptr<MemberDerived> member)


 : MainBase(std::move(member))


 , member_derived_(static_cast<decltype(member)::element_type*>(member_.get())) 


{}



正如你提到的,这要求protectedprotected getter存在。

据我所知,如果没有std::unique_ptr的实例,则会导致未定义的行为,除非有人传递了一个std :: unique_ptr,而该std :: unique_ptr实际上并未指向与element_type相同或从其派生的对象,在这种情况下,请使用 std :: unique_ptr的代码本身将是不安全的,因为如果实例被破坏而没有事先移动,它将导致不确定的行为。

关于你第三种方法:

我认为在C++17中技术上这是允许的,因为当member_derived_的存储初始化。

但是当前的C 20草案删除了此异常,因此member_derived_的生存期仅在其空虚初始化之后才开始,从而使它使用了未定义的行为。

作者:
...