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

2022-01-12 00:00:00 constructor c++ mfc

MFC 的根对象 CObject 的复制构造函数和赋值默认是禁用的.

The MFC's root object CObject's copy constructor and assignment are disabled by default.

  • 在 MSDN 中,有一个描述

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

The standard C++ default class copy constructor does a member-by-member copy. The presence of the private CObject copy constructor guarantees a compiler error message if the copy constructor of your class is needed but not available. You must therefore provide a copy constructor if your class requires this capability.

  • 在CObject的源码中,有一条注释:
  • 禁用复制构造函数并默认赋值,因此如果您通过值传递对象或分配对象,您将得到编译器错误而不是意外行为.

    Disable the copy constructor and assignment by default so you will get compiler errors instead of unexpected behaviour if you pass objects by value or assign objects.

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

    My question is, what is the problem with the default bit-by-bit copy constructor for this CObject class? In my opinion, it would be better to give us the default copy constructor, and we could provide one if necessary (deep copy)

    推荐答案

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

    The default copy constructor is member-by-member, not bitwise.

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

    Most CObject-derived classes contain - and manage directly - some system resources, that have no reference counting or similar mechanism, so the choice was probably made with the default use case in mind.

    例如CGDIObject大致是:

    e.g. CGDIObject is roughly:

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

    这里的默认复制构造函数会很危险(导致双重破坏),提供正确"的复制构造函数非常困难和昂贵.

    The default copy constructor here would be dangerous (leading to double destruction), providing a "correct" copy constructor is surprisingly hard and expensive.

    另一个原因可能是大多数 CObject 派生类都打算进行变异,因此通过引用传递.缺少的副本构造函数将捕获改变副本而不是传递的对象的意外副本:

    Another reason may be that most CObject-derived classes are intended to be mutated, and thus passed by reference. A missing copy constructor will catch unintended copies that mutate a copy rather than the object passed:

    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 保持不变.

    Omitting the "&" gives you code that compiles well, but creates a temporary copy, modifies that, and then drops it, while mo remains unmodified.

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

    Quite many API's follow that pattern, as MFC doesn't rely on exceptions for error handling (for historic reasons: not all targeted compilers did support them well, and MFC requires a lot of extra resource handling that becomes painful with exceptions).

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

    I don't think these choices are good, e.g. derived classes should be allowed to use the default copy constructor if their members permit (and most members should permit).

    不过,该决定符合 MFC 的心态",以及 MFC 创建时间的要求/限制.

    The decision fits the "mindset" of MFC, though, and the requriements / restrictions of the time MFC was created.

相关文章