本文介绍了这是在C ++ 11中实现pimpl wth unique_ptr和move-semantics的正确方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 我还没有看到一个利用unique_ptr和move-semantics的pimpl示例。 我想添加一个CHelper类到STL派生容器,并使用pimpl隐藏什么CHelper。 这看起来是否正确? Derived.h class CDerived:public set< CSomeSharedPtr> ;, public CHelper { // ... } ` Helper.h //派生容器需要支持copy和move,所以CHelper也太多 class CHelper { private: class impl; unique_ptr< impl> ; public: // ---默认值:need cotr& cotr(完整类),以便使用unique_ptr< impl> CHelper(); 〜CHelper(); // --- copy CHelper(const CHelper& src); // copy constructor CHelper& operator =(const CHelper& src); //赋值运算符 // --- move CHelper(CHelper& src); // move constructor CHelper& operator =(CHelper& src); //移动操作符 // ---这里公开的方法 void SetModified(BOOL bSet = TRUE); }; Helper.cpp // ========================= class CHelper :: impl { public: BOOL m_bModified; //已经修改了容器(需要保存) // ...其他数据 impl(){m_bModified = FALSE;} / / --- copy cotr / assign impl(const impl& src) { * this = src; } void operator =(const impl& src) { m_bModified = src.m_bModified; // ...其他数据} // --- move cotr / assign ??我需要写移动cotr /分配?? }; // ============================ CHelper :: CHelper():pimpl (unique_ptr< impl>(new impl())){} CHelper ::〜CHelper(){} // --- copy :: CHelper(const CHelper& src):pimpl(unique_ptr< impl>(new impl(* src.pimpl))){} CHelper& CHelper :: operator =(const CHelper& src) { if(this!=& src) * pimpl = * src.pimpl; return * this; } // --- move CHelper :: CHelper(CHelper& src) { if(this!=& ; src) { pimpl = move(src.pimpl); // use move for unique_ptr src.pimpl.reset(nullptr); //将pimpl保存在已定义/可破坏状态} } CHelper& CHelper :: operator =(CHelper& src) { if(this!=& src) { pimpl = move(src.pimpl); // use'move'for unique_ptr src.pimpl.reset(nullptr); //将pimpl保留在已定义/可破坏状态} return * this; } 解决方案 CHelper 是一个 unique_ptr ,并且copy的默认实现是调用基础和成员的副本,默认实现 move 正在调用基数和成员的 move ,没有必要覆盖 CHelper ctors和分配。只是让默认做他们的工作。他们只是调用适当的unique_ptr move构造函数和运算符。 关于整合 CHelper 和 set< ...> 形成一个CDerived ...这不是一个规范设计( set 不是一个OOP类...,CHelper也不是),但如果正确使用可以工作(不要尝试为 CHelper *分配 CDerived 广告调用删除,否则您会以泪花结束)。只是没有足够的信息来了解它们的用途。 如果问题是我要求CHelper也能够复制,那么你应该更好的以类似这样的成语( #include 和使用命名空间 apart ...) class CHelper { struct impl { ..... }; public: //创建和初始化 CHelper():pimpl(new impl){} //移动:只保留默认值 CHelper(CHelper& a)=默认值; // copy:用impl的初始化初始化 CHelper(const CHelper& a):pimpl(new impl(* a.pimpl)){} CHelper& operator =(CHelper a)// note:pass by value and let compiler do the magics { pimpl = move(a.pimpl); // a now nullifyed,but that's OK,it's just a value return * this; } 〜CHelper()= default; // not really necessary private: unique_ptr< impl> ; }; 当然,您可以根据需要分别声明和实现。 $ b $ 你可以将 struct impl; 转换为 CHelper (以便unique_ptr有意义) declare struct CHelper :: impl 在其他地方(可能在CPP文件中,所有CHelper实现将完成)。 这里唯一需要注意的是,在CPP文件中,CHelper :: impl必须同时定义构造函数和析构函数,才能具有一致的unique_ptr实例化以调用impl expructor)在CPP文件内。否则,对于一些编译器,在包含CHelper声明的所有文件中都会遇到不完全类型使用错误的风险。 关于第二点 std :: set )这是C ++编程的一个有争议的方面。 由于不是C ++本身的原因,但是面向对象编程学校,继承 是表示可能的对象替换。 因为这样,因为通过基指针删除一个对象是UB,如果基本dtor不是虚拟的,因此使对象成为UB,OOP学校拒绝作为一个教条的任何没有虚拟dtor的类的继承和因为他们在开始计划时受到教育的方式,如果你这样做,他们就开始对你的火焰喷吐。 对我来说,这不是一个问题你的设计,但他们的理解C ++继承的意思并不意味着是一个,而是就像,而不是暗示对象替换在思考每个C ++类是一个OOP对象,而不是使用一个工具来做一些有用的事情,只需看看这里或这里,如果你想更多地澄清我的立场:C ++中的对象替换不是对象,而是方法依据方法,因为每个方法可以是虚拟的或不是独立的每隔一个)。也就是说,你可能需要与那些人合作,所以...评估利益和缺点,不跟随他们在自己最喜欢的宗教的实践。 I haven't yet seen a pimpl example that utilizes both unique_ptr and move-semantics.I want to add a CHelper class to STL derived containers and use pimpl to hide what CHelper does. Does this look right?Derived.hclass CDerived : public set<CSomeSharedPtr>, public CHelper {//...};`Helper.h// derived containers need to support both copy and move, so CHelper does too class CHelper { private: class impl; unique_ptr<impl> pimpl; public: //--- default: need both cotr & cotr (complete class) in order to use unique_ptr<impl> CHelper(); ~CHelper(); //--- copy CHelper(const CHelper &src); //copy constructor CHelper& operator=(const CHelper &src);//assignment operator //--- move CHelper(CHelper &&src); //move constructor CHelper& operator=(CHelper &&src);//move operator //--- expose public methods here void SetModified(BOOL bSet=TRUE); }; Helper.cpp//=========================== class CHelper::impl { public: BOOL m_bModified; //has the container been modified (needs to be saved) // ... other data impl() {m_bModified = FALSE;} //--- copy cotr/assign impl(const impl &src) { *this = src; } void operator=(const impl &src) { m_bModified = src.m_bModified; // ...other data } //--- move cotr/assign ?? do I need to write move cotr/assign ?? }; //============================ CHelper::CHelper() : pimpl(unique_ptr<impl>(new impl())) {}CHelper::~CHelper() {} //--- copy CHelper::CHelper(const CHelper &src) : pimpl(unique_ptr<impl>(new impl(*src.pimpl))) {}CHelper& CHelper::operator=(const CHelper &src) { if (this != &src) *pimpl = *src.pimpl; return *this; } //--- move CHelper::CHelper(CHelper &&src) { if (this != &src) { pimpl = move(src.pimpl); //use move for unique_ptr src.pimpl.reset(nullptr);//leave pimpl in defined / destructable state } } CHelper& CHelper::operator=(CHelper &&src) { if (this != &src) { pimpl = move(src.pimpl); //use 'move' for unique_ptr src.pimpl.reset(nullptr);//leave pimpl in defined / destructable state } return *this; } 解决方案 Considering that the only member of CHelper is a unique_ptr and that the default implementation of copy is calling the copy of the bases and of the members, the default implementation for move is calling the move of the bases and members, there is no need to override the CHelper ctors and assigns. Just let the default do their job. They'll just call the appropriate unique_ptr move constructor and operator.About putting together CHelper and a set<...> to form a CDerived... that's not a "canonical design" (set is not a OOP class..., and CHelper also isn't) but can work if used properly (don't try to assign a CDerived to a CHelper* ad call delete on it, or you will end in tears). Simply there not enough information to understand what they're purposed.If the problme is "I whant CHelper to be also able to copy", then you should probably betterto folow an idiom like this (#include and using namespace apart ...)class CHelper{ struct impl { ..... };public: // create and initialize CHelper() :pimpl(new impl) {} // move: just keep the default CHelper(CHelper&& a) = default; // copy: initialize with a copy of impl CHelper(const CHelper& a) :pimpl(new impl(*a.pimpl)) {} CHelper& operator=(CHelper a) //note: pass by value and let compiler do the magics { pimpl = move(a.pimpl); //a now nullifyed, but that's ok, it's just a value return *this; } ~CHelper() = default; //not really necessaryprivate: unique_ptr<impl> pimpl;};Of course, feel free to separate declarations and implementation as you need.EDIT following John Balcom comments.Yes, of course the code changes, but not in its substance.You just can forward declare struct impl; into CHelper (so that unique_ptr has a meaning), then declarestruct CHelper::impl somewhere else (may be in the CPP file where all CHelper immplementation will be done). The only attention, here, is that in the CPP file CHelper::impl must have both constructor and destructor defined, in order to have a coherent unique_ptr instantiation (that has to call the impl destructor) inside the CPP file. Otherwise there is the risk, with some compilers, to get an "incomplete type usage" error in all files that include the CHelper declaration.About the second point (deriving from std::set) this is a controversial aspect of C++ programming.For reason that are not pertinet to C++ itself, but with the Object Oriented Programming school, "inhertance" means "is a", and "is a" means "object subtitution possible".Because of that, since deleting an object via a base pointer is UB if the base dtor isn't virtual, thus making object soubstitution UB, the OOP school refuse as a dogma the inheritance of whatever class having no virtual dtor and because of the way they had been educated when they started to program, they start to spit their flame on you if you do that.To me, that's not a problem in your design, but their fault in understanding that C++ inheritance does not means "is a" but "is like" and does not imply object substitution (to me it is their fault in thinking every C++ class is an OOP object, not you using a tool to do something useful to you, just look here or here, if you want more clarification on my position: object substitution in C++ is not for "object" but method by method since every method can be virtual or not independently on every other). That's said, may be you have to work with those people, so ... evaluate pros and cons in not following them in the practice of their own favorite religion. 这篇关于这是在C ++ 11中实现pimpl wth unique_ptr和move-semantics的正确方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
09-25 09:33