为什么禁用CObject的复制构造函数和赋值

时间:2010-06-21 13:57:58

标签: c++ mfc constructor

默认情况下,MFC的根对象CObject的复制构造函数和赋值被禁用。

  

标准C ++默认类副本   构造函数按成员执行   复制。私人的存在   CObject复制构造函数保证一个   如果复制编译器错误消息   你的类的构造函数是必需的   但不可用。因此,你必须   提供复制构造函数,如果你的   class需要这种能力。

  • 在CObject的源代码中,有一条评论:
  

禁用复制构造函数和   默认情况下赋值,因此如果按值传递对象或分配对象,则会出现编译器错误而不是意外行为。

我的问题是,这个CObject类的默认逐位复制构造函数有什么问题?在我看来,最好给我们一个默认的拷贝构造函数,如果需要我们可以提供一个(深拷贝)

2 个答案:

答案 0 :(得分:6)

默认的复制构造函数是成员,而不是按位。

大多数CObject派生类包含 - 并直接管理 - 一些没有引用计数或类似机制的系统资源,因此可能会考虑默认用例。

e.g。 CGDIObject大致是:

class CGDIObject : public CObject
{
    HGDIOBJ m_handle;

    CGDIObject() : m_handle(0) {}
    // derived classes provide a create, attach etc.
   ~CGDIObject() { DeleteObject(m_handle); } 
}

此处的默认复制构造函数将是危险的(导致双重破坏),提供“正确”的复制构造函数非常困难且昂贵。

另一个原因可能是大多数CObject派生类都要进行变异,因此通过引用传递。丢失的副本构造函数将捕获异常副本,这些副本会改变副本而不是传递的对象:

class CMyObject : public CObject
{
   public:
      AttachFoo(FooHandle foo) { ... }
      AddBar() { ... }
};

bool InitMySession(CMyObject & obj)
{
    obj.AttachFoo(CreateRawFoo());   
    obj.AddBar();
    obj.AddBar();
}

// ...
CMyObj mo;
InitMySession(mo);

省略“&”为您提供编译良好的代码,但创建一个临时副本,修改它,然后删除它,而mo保持不变。

很多API遵循这种模式,因为MFC不依赖于错误处理的异常(出于历史原因:并非所有目标编译器都支持它们,并且MFC需要大量额外的资源处理,这会因异常而变得痛苦) 。


我不认为这些选择是好的,例如如果派生类允许(并且大多数成员应该允许),则应允许派生类使用默认的复制构造函数。

该决定符合MFC的“心态”,以及MFC创建时间的要求/限制。

答案 1 :(得分:3)

请考虑以下事项:

class CMyHandle : public CObject
{
    HANDLE hWin32;
public:
    CMyHandle()
    {
        hWin32 = SomeFunctionThatMakesHandle();
    }
    ~CMyHandle()
    {
        CloseHandle(hWin32);
    }
};

现在,如果您复制CMyHandle,则句柄最终会被关闭两次,并且在其中任何一个CMyHandle实例被销毁后,另一个实例将变为无效。

由于大量MFC的类管理句柄,因此强制类的创建者显式覆盖以创建复制构造函数和复制赋值运算符是有意义的。

编辑:例如:

int main()
{
    CMyHandle h1; //CMyHandle's constructor initializes the handle
    {
        CMyHandle h2(h1); //Memberwise copy into h2. In this case, it copies the
                          //handle
    } //h2 destroyed, closes handle
    //h1 is now invalid (because the underlying handle was closed)!
}