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