[c++11]复制和移动操作

参考:

Copy Constructors and Copy Assignment Operators (C++)

Move Constructors and Move Assignment Operators (C++)

复制和移动操作是c++11新增的特性

  • 复制指的是将一个对象的值复制给另一个对象
  • 移动指的是将右值对象的资源通过移动方式(而不是复制)赋值给一个左值对象

每种操作最好同时实现构造器和运算符方式

复制构造器和复制赋值运算符

语法如下:

class Copy {
public:
    // 声明复制构造器
    Copy(const Copy &);

    // 声明复制赋值构造器
    Copy &operator=(const Copy &other);
};

设置参数为const以避免操作过程中意外修改原对象的值

示例如下:创建类MemoryBlock,设置字符数组_data和数组长度_length

class MemoryBlock {
public:

    // Simple constructor that initializes the resource.
    MemoryBlock(int length, const char *data);

    // Destructor.
    ~MemoryBlock();

    // Copy constructor.
    MemoryBlock(const MemoryBlock &other);

    // Copy assignment operator.
    MemoryBlock &operator=(const MemoryBlock &other);

    // Retrieves the length of the data resource.
    size_t Length() const;

    // print _data
    void print();

private:
    size_t _length; // The length of the resource.
    char *_data; // The resource.
};

创建了复制构造器和复制赋值构造器,实现如下:

MemoryBlock::MemoryBlock(int length, const char *data) {
    _length = length;
    _data = (char *) malloc(_length * sizeof(char));
    std::copy(data, data + _length, _data);

    std::cout << "In MemoryBlock: length = " << _length << " _data = " << _data << std::endl;
}

MemoryBlock::~MemoryBlock() {
    std::cout << "In ~MemoryBlock(). length = " << _length << ".";
    if (_data != nullptr) {
        std::cout << " Deleting resource.";
        // Delete the resource.
        delete[] _data;
    }
    std::cout << std::endl;
}

MemoryBlock::MemoryBlock(const MemoryBlock &other) : _length(other._length), _data(new char[other._length]) {
    std::cout << "In MemoryBlock(const MemoryBlock&). length = "
              << other._length << ". Copying resource." << std::endl;


    std::copy(other._data, other._data + _length, _data);
}

MemoryBlock &MemoryBlock::operator=(const MemoryBlock &other) {
    std::cout << "In operator=(const MemoryBlock&). length = "
              << other._length << ". Copying resource." << std::endl;

    if (this != &other) {
        // Free the existing resource.
        delete[] _data;

        _length = other._length;
        _data = new char[_length];
        std::copy(other._data, other._data + _length, _data);
    }
    return *this;
}

size_t MemoryBlock::Length() const {
    return _length;
}

void MemoryBlock::print() {
    std::cout << "Print MemoryBlock: length = " << _length << " _data = " << _data << std::endl;
}

使用函数std::copy复制数组值

移动构造器和移动赋值运算符

移动操作用于将右值对象的内容移动到当前对象中,操作过程不需要复制,避免了额外的内存分配和解析操作,更加有效率

语法如下:

class Move {
public:
    // 声明移动构造器
    Move(Move &&);

    // 声明移动赋值构造器
    Move &operator=(Move &&other);
};

参数为Move类型的右值引用,使用过程中去除const关键字,能够修改参数对象的值以避免资源的重复释放

同上面一样,创建类MemoryBlock,设置字符数组_data和数组长度_length

class MemoryBlock2 {
public:

    // Simple constructor that initializes the resource.
    MemoryBlock2(int length, const char *data);

    // Destructor.
    ~MemoryBlock2();

    // Copy constructor.
    MemoryBlock2(MemoryBlock2 &&other);

    // Copy assignment operator.
    MemoryBlock2 &operator=(MemoryBlock2 &&other);

    // Retrieves the length of the data resource.
    size_t Length() const;

    // print _data
    void print();

private:
    size_t _length; // The length of the resource.
    char *_data; // The resource.
};

实现如下:

MemoryBlock2::MemoryBlock2(int length, const char *data) {
    _length = length, ;
    _data = (char *) malloc(_length * sizeof(char));
    std::copy(data, data + _length, _data);

    std::cout << "In MemoryBlock2: length = " << _length << " _data = " << _data << std::endl;
}

MemoryBlock2::~MemoryBlock2() {
    std::cout << "In ~MemoryBlock2(). length = " << _length << ".";
    if (_data != nullptr) {
        std::cout << " Deleting resource.";
        // Delete the resource.
        delete[] _data;
    }
    std::cout << std::endl;
}

MemoryBlock2::MemoryBlock2(MemoryBlock2 &&other) : _data(nullptr), _length(0) {
    std::cout << "In MemoryBlock2(MemoryBlock2 &&). length = "
              << other._length << ". Moving resource." << std::endl;

    // Copy the data pointer and its length from the
    // source object.
    _data = other._data;
    _length = other._length;

    // Release the data pointer from the source object so that
    // the destructor does not free the memory multiple times.
    other._data = nullptr;
    other._length = 0;
}

MemoryBlock2 &MemoryBlock2::operator=(MemoryBlock2 &&other) {
    std::cout << "In operator=(MemoryBlock2 &&). length = "
              << other._length << "." << std::endl;

    if (this != &other) {
        // Free the existing resource.
        delete[] _data;

        // Copy the data pointer and its length from the
        // source object.
        _data = other._data;
        _length = other._length;

        // Release the data pointer from the source object so that
        // the destructor does not free the memory multiple times.
        other._data = nullptr;
        other._length = 0;
    }
    return *this;
}

size_t MemoryBlock2::Length() const {
    return _length;
}

void MemoryBlock2::print() {
    std::cout << "Print MemoryBlock2: length = " << _length << " _data = " << _data << std::endl;
}

移动操作如下:

  1. 释放当前对象的已有资源
  2. 复制移动对象的指针和长度
  3. 释放移动对象对资源的引用
  4. 对于移动赋值运算符而言,还应该考虑赋值操作是否指向自身

测试

在向量中插入对象时,移动操作能够提高性能,测试如下:

int main() {
    // Create a vector object and add a few elements to it.
    std::vector<MemoryBlock> v;
    v.emplace_back(MemoryBlock(3, "123"));
    v.emplace_back(MemoryBlock(5, "12345"));

    // Insert a new element into the second position of the vector.
    v.insert(v.begin() + 1, MemoryBlock(2, "45"));
更加有效率
//    std::vector<MemoryBlock2> v2;
//    v2.emplace_back(MemoryBlock2(3, "123"));
//    v2.emplace_back(MemoryBlock2(5, "12345"));
//
//    // Insert a new element into the second position of the vector.
//    v2.insert(v2.begin() + 1, MemoryBlock2(2, "45"));
}

相比较于复制操作,移动操作不需要重新分配字符数组内存,更加有效率

优化

同时实现了移动构造器和移动赋值运算符后,可以在构造器中调用赋值运算符进行优化,使用函数std::move修改如下:

// Move constructor.
MemoryBlock(MemoryBlock&& other): _data(nullptr), _length(0)
{
   *this = std::move(other);
}