为什么我们在c++中需要复制构造函数和赋值运算符
Why do we need exactly the copy constructor and assignment operator in c++
复制构造函数和赋值运算符都从一个对象复制到另一个对象,那么为什么我们需要两者呢?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
运算符。
因此,我们需要同时提供副本构造和副本分配。
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 为什么在C++中使用私有复制构造函数与删除复制构造函数
- 当从函数参数中的临时值调用复制构造函数时
- 如果有一个模板构造函数只有一个泛型参数,为什么我必须有一个复制构造函数
- 为什么需要复制构造函数,在哪些情况下它们非常有用
- 使用仅使用一次的变量调用的复制构造函数.这可能是通过调用move构造函数进行编译器优化的情况吗
- 为什么类中的ostringstream类型的成员会导致";调用隐含删除复制构造函数";错误
- 复制构造函数、赋值运算符C++
- std::ofstream 作为类成员删除复制构造函数?
- 复制构造函数C++无法正确复制指针
- 关于复制构造函数的一个棘手问题
- 为什么调用复制构造函数而不是移动构造函数?
- 填充上编译器生成的复制构造函数之间的不一致
- C++ 对象指针数组的复制构造函数
- C++ 基本 CTOR 说明 - 为什么不调用赋值/复制构造函数
- 防止在复制构造函数中隐式调用基构造函数
- 为用户定义的类正确调用复制构造函数/赋值运算符
- 具有已删除移动和复制构造函数的类的就地构造
- 复制构造函数隐式转换问题
- 复制构造函数中的递归调用