C++ 中的移动构造函数

Jinku Hu 2021年6月28日
C++ 中的移动构造函数

本文将介绍如何在 C++ 中使用移动构造函数。

使用移动构造函数为 C++ 中的类提供有效的复制控制

类的复制控制定义了指定当类对象被复制、移动、分配或销毁时会发生什么的核心功能。这些函数有特殊的 C++ 命名法,比如复制构造函数和移动构造函数定义了一个对象如何用另一个相同类型的对象初始化。复制赋值和移动赋值函数定义了如何将对象分配给相同类型的对象。Destructor 处理在对象超出范围时执行的例程。其中一些函数最有可能由用户定义,但如果不是,则编译器本身会创建默认原型。

当类对象管理动态内存并且数据非常大时,复制操作可能是计算密集型的。它们会消耗大量影响性能的资源。因此,他们经常使用移动构造函数来重新分配动态数据,而无需将其复制到新的内存位置。它是通过将旧对象的指针分配给新初始化或分配的对象的相应成员来实现的。请注意,以下示例不包含移动构造函数,它会导致多次调用复制构造函数,后者委托给默认构造函数。

#include <iostream>
#include <vector>

using std::cout; using std::endl;
using std::vector; using std::cin;

class MyClass {
private:
    int* data;

public:
    explicit MyClass(int d) {
        data = new int;
        *data = d;
        cout << "Constructor 1 is called" << endl;
    };

    MyClass(const MyClass& source): MyClass(*source.data) {
        cout << "Copy Constructor is called " << endl;
    }

    int getData() const {
        return *data;
    }

    ~MyClass() {
        delete data;
        cout << "Destructor is called" << endl;
    }

};

void printVec(const vector<MyClass> &vec) {

    for (const auto & i : vec) {
        cout << i.getData() << " ";
    }
    cout << endl;
}

int main() {
    vector<MyClass> vec;

    vec.push_back(MyClass(10));
    vec.push_back(MyClass(11));
    printVec(vec);
    cout << "------------------" << endl;

    return EXIT_SUCCESS;
}

输出:

Constructor 1 is called
Constructor 1 is called
Copy Constructor is called
Destructor is called
Constructor 1 is called
Constructor 1 is called
Copy Constructor is called
Constructor 1 is called
Copy Constructor is called
Destructor is called
Destructor is called
10 11
------------------
Destructor is called
Destructor is called

一旦我们定义了一个移动构造函数,它通常应该将 r 值引用作为第一个参数(用 && 表示法表示),随着 MyClass 类型的新元素的添加,向量初始化变得更加有效。由于移动构造函数不分配新内存并接管传递对象的位置,因此需要将 nullptr 分配给前一个对象的成员。否则,析构函数将尝试两次释放相同的内存位置,从而引发运行时错误。

#include <iostream>
#include <vector>

using std::cout; using std::endl;
using std::vector; using std::cin;

class MyClass {
private:
    int* data;

public:
    explicit MyClass(int d) {
        data = new int;
        *data = d;
        cout << "Constructor 1 is called" << endl;
    };

    MyClass(const MyClass& source): MyClass(*source.data) {
        cout << "Copy Constructor is called " << endl;
    }

    MyClass(MyClass&& source) noexcept : data(source.data) {
        source.data = nullptr;
        cout << "Move Constructor is called" << endl;
    }

    int getData() const {
        return *data;
    }

    ~MyClass() {
        delete data;
        cout << "Destructor is called" << endl;
    }

};

void printVec(const vector<MyClass> &vec) {

    for (const auto & i : vec) {
        cout << i.getData() << " ";
    }
    cout << endl;
}

int main() {
    vector<MyClass> vec;

    vec.push_back(MyClass(10));
    vec.push_back(MyClass(11));
    printVec(vec);
    cout << "------------------" << endl;

    return EXIT_SUCCESS;
}

输出:

Constructor 1 is called
Move Constructor is called
Destructor is called
Constructor 1 is called
Move Constructor is called
Move Constructor is called
Destructor is called
Destructor is called
10 11
------------------
Destructor is called
Destructor is called
Author: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn

相关文章 - C++ Class