关于c ++:这是在C ++ 11中实现pimpl wth unique_ptr和move-semantics的正确方法

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;  
}


考虑到CHelper的唯一成员是unique_ptr并且默认的copy实现是调用bases和members的副本,默认的move实现是调用bases和members的move,因此不需要重写CHelper的ctors和assignments。让默认值完成他们的工作。他们只需要调用适当的唯一移动构造函数和运算符。

关于把CHelperset<...>组合在一起形成一个循环…这不是"规范设计"(set不是oop类…,而chelper也不是),但如果使用得当,它就可以工作(不要试图将CDerived分配给CHelper*的广告调用delete,否则你会落泪)。只是没有足够的信息来理解它们的目的。

如果问题是"我想切尔珀也能复制",那么你最好像这样的成语(#includeusing namespace分开…)

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评论。

是的,当然代码会改变,但实质上不会改变。您只需将声明struct impl;转发到CHelper中(这样唯一的指针就有意义了),然后声明结构CHelper::impl(可能在cpp文件中,所有chelper嵌入都将在其中完成)。

这里唯一要注意的是,在cpp文件中,chelper::impl必须同时定义构造函数和析构函数,以便在cpp文件中有一个一致的、唯一的实例化(必须调用impl析构函数)。否则,对于某些编译器来说,在包含chelper声明的所有文件中都存在获取"不完整类型使用"错误的风险。

关于第二点(源自EDCOX1〔13〕),这是C++编程的一个有争议的方面。由于与C++本身无关,但在面向对象程序设计学派中,"继承"指的是"A",而"是"则意味着"对象细分"。因此,由于通过基指针删除一个对象是ub,如果基dtor不是虚拟的,因此使对象变为抽象ub,因此oop学校拒绝将没有虚拟dtor的任何类的继承作为教条,并且由于他们在开始编程时接受的教育方式,如果你这样做,他们就会开始向你吐出火焰。那。

对我来说,这不是你的设计中的问题,但他们理解C++继承的错误并不意味着"是"而是"类似",并不意味着对象替换(对我来说)这是他们的错误,认为每个C++类都是OOP对象,而不是使用工具来做对你有用的东西,只要看看这里或这里,如果你想要更多的澄清。在我的位置上:C++中的对象替换不是"对象"而是方法的方法,因为每种方法都可以是虚拟的或独立的。也就是说,也许你必须和那些人一起工作,所以…在他们自己喜爱的宗教实践中,评估不遵守这些原则的利弊。