在C++11及之后的标准中,移动语义成为提升性能的核心特性之一。本文将通过实现一个简单的自定义动态数组(类似std::vector)来演示如何为自己的容器类添加移动构造函数、移动赋值运算符以及相应的资源管理逻辑。
1. 设计思路
我们定义一个名为 SimpleVector 的模板类,用于存储任意类型的数据。核心成员包括:
T* data_:指向动态分配的数组。size_t size_:当前元素个数。size_t capacity_:已分配的容量。
为了实现移动语义,需要满足以下条件:
- 移动构造函数:将源对象的资源指针转移到新对象,源对象置为安全状态。
- 移动赋值运算符:先释放目标对象已有资源,再将源对象的资源指针转移过去,源对象置安全状态。
- 自定义析构函数:释放资源。
2. 代码实现
#include <cstddef>
#include <utility>
#include <stdexcept>
#include <iostream>
template<typename T>
class SimpleVector {
public:
SimpleVector() noexcept : data_(nullptr), size_(0), capacity_(0) {}
// 析构函数
~SimpleVector() { delete[] data_; }
// 复制构造(仅示例,未实现移动复制)
SimpleVector(const SimpleVector& other)
: data_(other.size_ ? new T[other.capacity_] : nullptr),
size_(other.size_), capacity_(other.capacity_) {
std::copy(other.data_, other.data_ + other.size_, data_);
}
// 移动构造函数
SimpleVector(SimpleVector&& other) noexcept
: data_(other.data_), size_(other.size_), capacity_(other.capacity_) {
// 置源对象为空状态
other.data_ = nullptr;
other.size_ = 0;
other.capacity_ = 0;
}
// 移动赋值运算符
SimpleVector& operator=(SimpleVector&& other) noexcept {
if (this != &other) {
delete[] data_; // 释放已有资源
data_ = other.data_;
size_ = other.size_;
capacity_ = other.capacity_;
// 置源对象为空
other.data_ = nullptr;
other.size_ = 0;
other.capacity_ = 0;
}
return *this;
}
// 追加元素
void push_back(const T& value) {
if (size_ == capacity_) reserve(capacity_ == 0 ? 1 : capacity_ * 2);
data_[size_++] = value;
}
// 获取大小
size_t size() const noexcept { return size_; }
// 索引访问
T& operator[](size_t index) {
if (index >= size_) throw std::out_of_range("Index out of range");
return data_[index];
}
const T& operator[](size_t index) const {
if (index >= size_) throw std::out_of_range("Index out of range");
return data_[index];
}
private:
void reserve(size_t new_capacity) {
if (new_capacity <= capacity_) return;
T* new_data = new T[new_capacity];
for (size_t i = 0; i < size_; ++i) new_data[i] = std::move(data_[i]);
delete[] data_;
data_ = new_data;
capacity_ = new_capacity;
}
T* data_;
size_t size_;
size_t capacity_;
};
3. 使用示例
int main() {
SimpleVector <int> v1;
for (int i = 0; i < 5; ++i) v1.push_back(i);
// 通过移动构造创建 v2
SimpleVector <int> v2 = std::move(v1);
std::cout << "v2.size() = " << v2.size() << std::endl; // 输出 5
std::cout << "v1.size() = " << v1.size() << std::endl; // 输出 0,已置空
// 继续使用 v2
for (size_t i = 0; i < v2.size(); ++i) std::cout << v2[i] << ' ';
std::cout << std::endl;
// 移动赋值
SimpleVector <int> v3;
v3 = std::move(v2);
std::cout << "v3.size() = " << v3.size() << std::endl; // 输出 5
std::cout << "v2.size() = " << v2.size() << std::endl; // 输出 0
return 0;
}
4. 关键点回顾
- 移动构造 必须使用
noexcept以便在标准容器中安全使用。 - 资源转移 时要把源对象置为“空”状态(指针为
nullptr,大小为0)。 - 自定义析构 负责真正释放资源,防止内存泄漏。
- 移动赋值 前先释放自身已有资源,避免资源泄漏。
通过上述实现,SimpleVector 就具备了完整的移动语义,能够在大规模数据移动时显著提升性能,且使用方式与 std::vector 类似。