为什么我们在c++中需要复制构造函数和赋值运算符

Why do we need exactly the copy constructor and assignment operator in c++

本文关键字:复制 构造函数 赋值运算符 我们 c++ 为什么      更新时间:2023-10-16

复制构造函数和赋值运算符都从一个对象复制到另一个对象,那么为什么我们需要两者呢?Copyconstructor意味着它创建一个新对象并从另一个现有对象复制内容,赋值运算符将内容从一个现有的对象复制到已经存在的对象。

所以最终两者都是将内容从一个对象复制到另一个对象,那么为什么我们需要两者,我们只能有一个权利。

因为构造和赋值是不同的。复制构造函数是涉及的,例如,当您通过值传递参数或用另一个值构造对象时:

f(T o) {
...}
T object;
f(object); // copy construction to pass the argument
T object2(object); // construct a T from another T

而赋值是当你已经有了两个对象,并且想把其中一个复制到另一个中时:

T object;
...
T object2;
...
object = object2;

这些事情以不同的表达方式发生。

因为它们是不同的东西。

当您想从另一个对象构造新对象时,将调用Copy ctor。

当您想从另一个对象分配现有对象时,将调用分配运算符,这意味着您可能需要在执行分配之前销毁/释放现有对象所拥有的一些资源。

您不应该创建虚拟对象只是为了在下一步中执行赋值,每次都需要一个副本。

特别是如果对象构造即使在默认构造的情况下也是昂贵的。

但是复制c'tors的最终原因是,用户定义类型的传递值是不可能的。然后我们就没有RAII了。

至于赋值运算符,想象一下在没有它的情况下编写类。泛型(模板)代码会是什么样子?

许多通用算法可以对仅可赋值的类进行操作,但如果没有这一功能,在再次"构造"对象之前,您必须自己调用它们的"tor"。

template <calls T)
void assign(T& a, T& b)
{
  a.~T();
  new (&a) (b);
}

基本上,这对阶级提出了一个不合理的要求,即他们的名字必须是公开的。或者他们必须让你的算法为friend

此外,这是不安全的。构造函数可能抛出,这就是它们报告错误的方式,如果T副本c'tor抛出,那么a在析构函数调用后处于未定义状态
问题是,您决定释放a的状态。如果我们使用赋值运算符,赋值仍然会抛出,但它可以确保a还没有释放它的状态。

这里有两种不同的情况,

  • 使用现有对象的内容初始化新对象(1)
  • 将现有对象的内容复制/传输到另一个现有对象(2)

存在(1)适用和(2)适用的不同场景。它们不一样。

例如,如果我们想将一个对象拥有的资源(例如内存)的所有权与另一个具有相同类型的对象共享(shared_ptr是一个很好的例子),我们需要在该类上实现一个适当的copy-assignment运算符。

因此,我们需要同时提供副本构造和副本分配。