📜  C++中的复制和交换惯用语

📅  最后修改于: 2021-05-30 11:48:49             🧑  作者: Mango

在深入探讨之前,让我们首先看一下我们使用的常规“重载赋值运算符”。

// Simple C++ program to demonstrate overloading of
// assignment operator.
#include 
#include 
using namespace std;
  
class anyArrayClass
{
    int size;
    int *ptr;
public:
  
    // Initializer list
    anyArrayClass(int s=0):size(s),
        ptr(size? new int[size]:nullptr) {}
  
    // Copy constructor
    anyArrayClass(const anyArrayClass& obj):size(obj.size),
        ptr(size? new int[size]:nullptr)
    {
        memmove(ptr, obj.ptr, size*sizeof(int));
    }
  
    // Overloaded assignment operator
    anyArrayClass& operator=(const anyArrayClass& obj)
    {
        // self assignment check
        if (this != &obj)
        {
            delete ptr;
            size = obj.size;
            ptr = size? new int[size]: nullptr;
            memmove(ptr, obj.ptr, size*sizeof(int));
            return *this;
        }
    }
    ~anyArrayClass()
    {
        delete[] ptr;
    }
}

上面的赋值运算符执行以下操作:
1.自我分配检查。
2.如果没有分配给自己,那么它会执行以下操作。
a)释放分配给this-> ptr的内存
b)将新内存分配给this-> ptr并复制值
c)返回* this

上述方法的缺点:

  1. 自我分配检查:很少进行自我分配,因此在大多数情况下,自我分配检查均不相关。这只会减慢代码的速度。
  2. 内存的重新分配和分配:可以看出,首先分配了内存(使指针悬空),然后分配了新的内存块。现在,如果由于某种原因未分配内存并引发异常,则’this-> ptr’将悬空指向已释放的内存。该方案应该是分配成功,或者根本不应该更改对象。

复制和交换方法的作用就在这里。这种方法很好地解决了上述问题,并且还提供了代码可重用性的范围。让我们看看它到底是什么。

考虑以下代码:

// Simple C++ program to demonstrate use of copy-and-swap
// idiom by improving above code.
#include 
#include 
using namespace std;
  
class anyArrayClass
{
    int size;
    int *ptr;
public:
    anyArrayClass(int s=0):size(s),
     ptr(size? new int[size]:nullptr) {}
  
    // Copy constructor
    anyArrayClass(const anyArrayClass& obj):size(obj.size),
                           ptr(size? new int[size]:nullptr)
    {
        memmove(ptr, obj.ptr, size*sizeof(int));
    }
  
    friend void swap(anyArrayClass& obj1, anyArrayClass& obj2)
    {
        std::swap(obj1.size, obj2.size);
        std::swap(obj1.ptr, obj2.ptr);
    }
      
    // overloaded assignment operator
    // argument passed by value. calls copy ctor
    anyArrayClass& operator=(anyArrayClass obj)    
    {
        // calling friend function
        swap(*this, obj);
        return *this;
    }
  
    ~anyArrayClass()
    {
        delete[] ptr;
    }
}

在上面的示例中,’ 运算符=()’的参数通过值传递,该值调用复制构造函数以创建’ 运算符=()’本地的anyArrayClass对象。临时对象的值比’* this’对象交换(赋值运算符调用的LHS)。

好处:

  1. 由于参数是通过值传递的,因此不再需要进行自我分配检查(这意味着不再需要内存分配)。此外,由于自我分配非常少见,因此在自我分配的情况下进行复制的开销应该不会成为问题。
  2. 现在,由于将复制构造函数用于创建临时对象,因此仅在完全创建临时对象的情况下才进行交换。基本上,我们在此处手动执行的操作是编译器在此处为我们执行的操作。
  3. 代码的可重用性:我们可以看到’ 运算符=()’的主体中没有太多代码,而是使用复制构造函数和swap函数来完成这项工作。
要从最佳影片策划和实践问题去学习,检查了C++基础课程为基础,以先进的C++和C++ STL课程基础加上STL。要完成从学习语言到DS Algo等的更多准备工作,请参阅“完整面试准备课程”