为什么要禁用CObject的拷贝构造函数和赋值 [英] Why disable CObject's copy constructor and assignment

查看:142
本文介绍了为什么要禁用CObject的拷贝构造函数和赋值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

MFC的根对象CObject的副本构造函数和赋值在默认情况下是禁用的。






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





  • 在CObject的源代码中,有一个注释:




禁用复制构造函数和
分配默认情况下,如果你通过值传递对象或分配对象,你会得到编译器错误而不是意外的行为。


,这个CObject类的默认逐位复制构造函数的问题是什么?

解决方案



大多数CObject派生类包含 - 并直接管理一些系统资源,没有引用计数或类似的机制,所以选择是考虑到默认用例。



例如CGDIObject大致是:

  class CGDIObject:public CObject 
{
HGDIOBJ m_handle;

CGDIObject():m_handle(0){}
//派生类提供了一个create,attach等方法
〜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的时间的要求/限制。


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

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.

  • In CObject's source code, there is a comment:

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.

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.

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.

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.

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

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

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).

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

这篇关于为什么要禁用CObject的拷贝构造函数和赋值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆