C++ 中的移动构造函数

Jinku Hu 2023年10月12日
C++ 中的移动构造函数

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

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

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

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

#include <iostream>
#include <vector>

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

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::cin;
using std::cout;
using std::endl;
using std::vector;

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
作者: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

DelftStack.com 创始人。Jinku 在机器人和汽车行业工作了8多年。他在自动测试、远程测试及从耐久性测试中创建报告时磨练了自己的编程技能。他拥有电气/电子工程背景,但他也扩展了自己的兴趣到嵌入式电子、嵌入式编程以及前端和后端编程。

LinkedIn Facebook

相关文章 - C++ Class