Is this the right way to implement pimpl wth unique_ptr and move-semantics in C++11
我还没有看到一个PIMPL示例,它同时使用了惟一的指针和移动语义。
我想将一个chelper类添加到stl派生的容器中,并使用pimpl隐藏chelper所做的工作。
这个看起来对吗?
派生的1 2 3 4 | class CDerived : public set<CSomeSharedPtr>, public CHelper { //... }; |
`
帮助者1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // 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); }; |
Help.CPP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | //=========================== 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; } |
考虑到
关于把
如果问题是"我想切尔珀也能复制",那么你最好像这样的成语(
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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 necessary private: unique_ptr<impl> pimpl; }; |
当然,您可以根据需要自由地分离声明和实现。
编辑以下John Balcom评论。
是的,当然代码会改变,但实质上不会改变。您只需将声明
这里唯一要注意的是,在cpp文件中,chelper::impl必须同时定义构造函数和析构函数,以便在cpp文件中有一个一致的、唯一的实例化(必须调用impl析构函数)。否则,对于某些编译器来说,在包含chelper声明的所有文件中都存在获取"不完整类型使用"错误的风险。
关于第二点(源自EDCOX1〔13〕),这是C++编程的一个有争议的方面。由于与C++本身无关,但在面向对象程序设计学派中,"继承"指的是"A",而"是"则意味着"对象细分"。因此,由于通过基指针删除一个对象是ub,如果基dtor不是虚拟的,因此使对象变为抽象ub,因此oop学校拒绝将没有虚拟dtor的任何类的继承作为教条,并且由于他们在开始编程时接受的教育方式,如果你这样做,他们就会开始向你吐出火焰。那。
对我来说,这不是你的设计中的问题,但他们理解C++继承的错误并不意味着"是"而是"类似",并不意味着对象替换(对我来说)这是他们的错误,认为每个C++类都是OOP对象,而不是使用工具来做对你有用的东西,只要看看这里或这里,如果你想要更多的澄清。在我的位置上:C++中的对象替换不是"对象"而是方法的方法,因为每种方法都可以是虚拟的或独立的。也就是说,也许你必须和那些人一起工作,所以…在他们自己喜爱的宗教实践中,评估不遵守这些原则的利弊。