Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/opengl/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在C+中复制构造函数并=运算符重载+;:是否可以使用公共功能?_C++_Variable Assignment_Copy Constructor_C++ Faq - Fatal编程技术网

C++ 在C+中复制构造函数并=运算符重载+;:是否可以使用公共功能?

C++ 在C+中复制构造函数并=运算符重载+;:是否可以使用公共功能?,c++,variable-assignment,copy-constructor,c++-faq,C++,Variable Assignment,Copy Constructor,C++ Faq,因为它是一个复制构造函数 MyClass(const MyClass&); 和an=操作员过载 MyClass& operator = (const MyClass&); 如果代码几乎相同,参数相同,并且返回值不同,那么它们是否可以使用相同的函数?是。有两种常见的选择。一种通常不鼓励的方法是从复制构造函数显式调用运算符=: MyClass(const MyClass& other) { operator=(other); } 但是,在处理旧状态和自分

因为它是一个复制构造函数

MyClass(const MyClass&);
和an=操作员过载

MyClass& operator = (const MyClass&);

如果代码几乎相同,参数相同,并且返回值不同,那么它们是否可以使用相同的函数?

是。有两种常见的选择。一种通常不鼓励的方法是从复制构造函数显式调用
运算符=

MyClass(const MyClass& other)
{
    operator=(other);
}
但是,在处理旧状态和自分配引起的问题时,提供一个好的
操作符=
是一个挑战。此外,所有成员和基都会首先进行默认初始化,即使它们是从
other
分配的。这甚至可能不是对所有成员和基都有效,即使在有效的情况下,它在语义上是冗余的,并且可能实际上是昂贵的

越来越流行的解决方案是使用复制构造函数和交换方法实现
操作符=

MyClass& operator=(const MyClass& other)
{
    MyClass tmp(other);
    swap(tmp);
    return *this;
}
甚至:

MyClass& operator=(MyClass other)
{
    swap(other);
    return *this;
}
swap
函数通常编写起来很简单,因为它只交换内部的所有权,不必清理现有状态或分配新资源

复制和交换习惯用法的优点是,它是自动自分配安全的,并且如果交换操作不是抛出的,那么它也是强异常安全的

为实现强异常安全,“手写”赋值运算符通常必须在取消分配受让人的旧资源之前分配新资源的副本,以便在分配新资源时发生异常时,仍可返回到旧状态。所有这些都是免费的复制和交换,但通常更复杂,因此容易出错,从头开始

需要注意的一件事是确保swap方法是真正的swap,而不是使用复制构造函数和赋值运算符本身的默认
std::swap


通常使用memberwise
swap
<代码>标准::交换有效,所有基本类型和指针类型都保证“不抛出”。大多数智能指针也可以在无抛出保证的情况下进行交换。

复制构造函数对用作原始内存的对象执行首次初始化。赋值运算符OTOH用新值覆盖现有值。这通常涉及到丢弃旧资源(例如内存)和分配新资源

如果两者之间有相似之处,那就是赋值操作符执行销毁和复制构造。一些开发人员过去常常通过就地销毁然后放置副本构造来实际执行分配。然而,这是一个非常糟糕的想法。(如果这是在派生类赋值期间调用的基类的赋值运算符,该怎么办?)

如今,人们通常认为的标准成语是使用Charles建议的
swap

MyClass& operator=(MyClass other)
{
    swap(other);
    return *this;
}

它使用复制构造(注意,
other
被复制)和销毁(在函数末尾被销毁)——并且它也以正确的顺序使用它们:销毁之前的构造(可能失败)(不得失败)

有件事困扰着我:

MyClass& operator=(const MyClass& other)
{
    MyClass tmp(other);
    swap(tmp);
    return *this;
}
首先,当我的大脑在思考“复制”时,读“交换”这个词会刺激我的常识。此外,我对这个花哨把戏的目的提出了质疑。是的,在构建新的(复制的)资源时,任何异常都应该在交换之前发生,这似乎是一种确保所有新数据在启用之前都已填充的安全方法

那很好。那么,互换后发生的例外情况如何?(当临时对象超出作用域时旧资源被破坏)从分配用户的角度来看,操作失败了,但没有。它有一个巨大的副作用:复制确实发生了。只是一些资源清理失败了。即使从外部看操作似乎失败,目标对象的状态也已更改

因此,我建议用更自然的“转移”代替“交换”:

仍然存在临时对象的构造,但下一个直接操作是在将源的资源移动到目标之前释放目标的所有当前资源(并为null,这样它们就不会被双重释放)

我建议{construct,destruct,move},而不是{construct,move}。这是最危险的行动,是在其他一切都解决之后最后采取的行动

是的,销毁失败是两种方案中的一个问题。数据要么已损坏(在您认为不存在时复制),要么已丢失(在您认为不存在时释放)。失去总比堕落好。没有数据总比坏数据好


转移而不是交换。这是我的建议。

实际上,它们不是常见的操作。当复制构造函数第一次初始化对象的成员时,赋值操作符将覆盖现有值。考虑到这一点,从复制的C++中,Oracle <代码>运算符=/COD>实际上是非常糟糕的,因为它首先将所有值初始化为默认值,以便在以后的另一个对象的值上重写它们。有人可能会出现,但没有意识到你不仅仅是在表达个人的少数群体偏好,而是那些真正考虑过这一点的人已经达成的共识。而且,好吧,也许我错了,一些C++专家确实推荐它,但是我个人仍然会为某人提出一个挑战,为这个建议提供参考。我认为,如果某件事被广泛认为是最佳实践,那么最好是这样说(如果有人说它毕竟不是真正的最佳实践,那么再看一遍)。同样,如果有人问“是否可以在C++中使用互斥体”,我也不会说“一个相当好的互斥体”
MyClass& operator=(const MyClass& other)
{
    MyClass tmp(other);
    transfer(tmp);
    return *this;
}