C ++中赋值运算符重载的目的

时间:2013-02-15 17:55:26

标签: c++ operator-overloading assignment-operator

我试图理解在C ++中重载某些运算符的目的 从概念上讲,可以通过以下方式轻松实现赋值语句:

  • 销毁旧对象,然后复制新对象
  • 复制新对象的构造,然后与旧对象交换,然后销毁旧对象

事实上,复制和交换实现通常实际代码中的赋值实现。

那么,为什么C ++允许程序员重载赋值运算符,而不仅仅是执行上述操作?

是否打算允许分配比破坏+构造更快的场景?
如果是这样,那么什么时候发生?如果没有,那么它打算支持哪个用例?

6 个答案:

答案 0 :(得分:1)

1)参考计数

假设您有一个被重新计数的资源,并且它被包装在对象中。

void operator=(const MyObject& v) {
  this->resource = refCount(v->resource);
}

// Here the internal resource will be copied and not the objects.
MyObject A = B;

2)或者您只想复制没有花哨语义的字段。

void operator=(const MyObject& v) {
  this->someField = v->someField;
}

// So this statement should generate just one function call and not a fancy 
// collection of temporaries that require construction destruction.
MyObject A = B;

在这两种情况下,代码运行得更快。在第二种情况下,效果类似。

3)那么类型呢...... 使用运算符来处理为您的类型指定其他类型。

void operator=(const MyObject& v) {
  this->someField = v->someField;
}
void operator=(int x) {
  this->value = x;
}
void operator=(float y) {
  this->floatValue = y;
}

答案 1 :(得分:1)

首先,请注意“破坏旧对象后跟新对象的复制构造”并不是例外安全。

但重新“复制新对象的构造,然后与旧对象交换,然后销毁旧对象”,这是用于实现赋值运算符的交换习惯用法,它是如果正确完成,则异常安全。

在某些情况下,自定义赋值运算符可能比交换习惯用语更快。例如,除了较低级别的分配之外,不能真正交换POD类型的直接数组。因此,对于交换习惯用法,您可以预期与数组大小成比例的开销。

然而,历史上并没有太多关注交换和异常安全。

bjarne最初想要例外(如果我没记错的话),但他们直到1989年或之后才进入该语言。所以最初的c ++编程方式更侧重于作业。通过将0分配给this ... i 思考,失败的构造函数表示失败的程度,在那些日子里你的问题没有意义。这只是全部的任务。


类型地,一些对象具有身份,而其他对象具有价值。分配给值对象是有意义的,但对于标识对象,人们通常希望限制对象的修改方式。虽然这不需要自定义副本分配的能力(只是为了使其不可用),但是使用该功能,不需要任何其他语言支持。


并且我认为同样可以考虑任何其他具体原因:可能没有这样的理由真正需要一般能力,但一般能力足以涵盖所有,因此它降低了整体语言的复杂性。


比我的预感,回忆和直觉更能获得更明确答案的好消息来源是bjarne的“c ++的设计和演变”一书。

可能这个问题在那里有明确的答案。

答案 2 :(得分:1)

破坏旧物体,然后复制构造 新的,通常不会起作用。交换习语就是 除非班级提供特殊交换,否则保证不起作用 函数 - std::swap在其非专业化中使用赋值 实现,并直接在赋值运算符中使用它 将导致无休止的递归。

当然,用户可能想要做一些特别的事情,例如 例如,将赋值运算符设为私有。

最后,几乎可以肯定的是一个推翻理由: 默认赋值运算符必须与C。

兼容

答案 3 :(得分:1)

实际上,在看到juanchopanza的答案(被删除)后,我想我最终自己搞清楚了。

复制赋值运算符允许像basic_string这样的类避免在不再分配资源时再次使用它们(在本例中为内存)。

因此,当您分配给basic_string时,重载的复制赋值运算符将避免分配内存,并且只是将字符串数据直接复制到现有缓冲区。

如果必须再次销毁和构造对象,则必须重新分配缓冲区,对于小字符串来说,这可能会花费更多。

(注意vector也可以从中受益,但前提是它知道元素的复制构造函数永远不会抛出异常。否则它需要保持其异常安全并实际执行复制 - 并且 - 交换。)

答案 4 :(得分:0)

我经常将它用作转换构造函数,但已经存在对象。即将成员变量类型等分配给对象。

答案 5 :(得分:0)

它允许您使用其他类型的分配 你可以有一个 Person 类,它有一个赋值的赋值运算符。

但除此之外,您并不总是希望按原样复制所有成员。

默认分配仅执行浅拷贝 例如,如果类包含指针或锁,则您不总是希望从其他对象复制它们 通常当你有指针时你想要使用深层复制,并且可能创建指针指向的对象的副本。
如果你有锁,你希望它们特定于对象,并且你不想从另一个对象复制它们的状态。

如果您的类将指针作为成员保存,那么提供您自己的复制构造函数和赋值运算符实际上是一种常见的做法。