c++的移动语义

1 "std::move 就是用来榨干这种"即将消亡的局部变量"的最后价值的。它完美避免了毫无意义的内存申请和深拷贝!"

复制代码
#include <chrono>
#include <vector>

void performanceTest() {
    std::vector<std::string> vec;
    
    // 深拷贝版本
    auto start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < 10000; ++i) {
        std::string s(1000, 'x');
        vec.push_back(s);  // 深拷贝
    }
    auto end = std::chrono::high_resolution_clock::now();
    auto copy_time = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
    
    vec.clear();
    
    // move移动版本
    start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < 10000; ++i) {
        std::string s(1000, 'x');
        vec.push_back(std::move(s));  // 移动
    }
    end = std::chrono::high_resolution_clock::now();
    auto move_time = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
    
    std::cout << "深拷贝耗时: " << copy_time << "ms\n";
    std::cout << "移动耗时: " << move_time << "ms\n";
    std::cout << "加速比: " << (double)copy_time / move_time << "x\n";
}

核心原理:移动语义

复制代码
#include <iostream>
#include <vector>
#include <string>

class MyString {
private:
    char* data;
    size_t size;
    
public:
    // 构造函数
    MyString(const char* str) {
        size = strlen(str);
        data = new char[size + 1];
        strcpy(data, str);
        std::cout << "构造: " << data << std::endl;
    }
    
    // 深拷贝构造函数(昂贵)
    MyString(const MyString& other) {
        size = other.size;
        data = new char[size + 1];
        strcpy(data, other.data);
        std::cout << "深拷贝: " << data << std::endl;
    }
    
    // 移动构造函数(廉价)
    MyString(MyString&& other) noexcept {
        data = other.data;    // 直接接管指针
        size = other.size;
        other.data = nullptr; // 原对象置空
        other.size = 0;
        std::cout << "移动构造: " << data << std::endl;
    }
    
    ~MyString() {
        if (data) {
            std::cout << "析构: " << data << std::endl;
            delete[] data;
        }
    }
};

MyString createString() {
    MyString temp("即将消亡的局部变量");
    return temp;  // 自动移动(C++11起)
}

int main() {
    MyString s = createString();  // 移动构造,不是深拷贝
    return 0;
}

黄金法则

✅ 应该使用 std::move 的场景

复制代码
// 1. 转移临时/即将消亡的变量
std::vector<int> v = std::move(largeVector);  // largeVector不再使用

// 2. 容器操作
std::vector<std::string> vec;
vec.push_back(std::move(str));  // str之后不再需要

// 3. 实现移动构造函数/赋值运算符
MyClass(MyClass&& other) noexcept : ptr(std::move(other.ptr)) {}

// 4. 交换操作
std::swap(a, b);  // C++11起使用移动语义

❌ 不应该使用 std::move 的场景

复制代码
// 1. 移动后仍使用对象
auto s = std::move(str);
std::cout << str;  // ❌ 未定义行为!str已被掏空

// 2. const对象
const std::string constStr = "hello";
auto s = std::move(constStr);  // ❌ 实际上会拷贝,const不能移动

// 3. 返回值优化(RVO)已自动移动
std::vector<int> getVector() {
    std::vector<int> v{1,2,3};
    return v;  // ✅ 自动移动,不要写 return std::move(v)
}

// 4. 小对象(int, char等)移动无意义
int a = 10;
int b = std::move(a);  // 无意义,只是拷贝

标准库中的移动语义

复制代码
#include <memory>
#include <thread>
#include <fstream>

// unique_ptr(只能移动,不能拷贝)
std::unique_ptr<int> p1 = std::make_unique<int>(42);
std::unique_ptr<int> p2 = std::move(p1);  // 转移所有权

// thread
std::thread t1([](){ /* ... */ });
std::thread t2 = std::move(t1);  // 转移线程所有权

// fstream
std::ofstream f1("test.txt");
std::ofstream f2 = std::move(f1);  // 转移文件句柄

基于移动语义构建资源内存管理器:

方案一:手动实现"移动语义"(五项法则模式)

对于需要管理原始 C 风格 API 句柄(如 TensorRT 的 ICudaEngine 或自建的显存 Buffer)的情况,建议将其实现为仅可移动 (Move-only) 的类,以确保资源的唯一所有权并避免昂贵的深度拷贝。

复制代码
#include <iostream>
#include <utility>

class GPUBuffer {
private:
    float* data_ = nullptr;
    size_t size_ = 0;

public:
    // 1. 构造函数:获取资源
    explicit GPUBuffer(size_t n) : size_(n) {
        // 模拟显存分配 (例如 cudaMalloc)
        data_ = new float[n]; 
        std::cout << "Allocated " << n << " elements.\n";
    }

    // 2. 析构函数:释放资源(确保无内存泄漏)
    ~GPUBuffer() {
        if (data_) {
            delete[] data_; // 模拟资源释放 (例如 cudaFree)
            std::cout << "Resources released.\n";
        }
    }

    // 3. 禁用拷贝:防止多个对象指向同一块内存导致重复释放
    GPUBuffer(const GPUBuffer&) = delete;
    GPUBuffer& operator=(const GPUBuffer&) = delete;

    // 4. 移动构造函数:高效转移所有权
    GPUBuffer(GPUBuffer&& other) noexcept : data_(other.data_), size_(other.size_) {
        other.data_ = nullptr; // 将原对象置为空,防止析构时释放资源
        other.size_ = 0;
    }

    // 5. 移动赋值运算符
    GPUBuffer& operator=(GPUBuffer&& other) noexcept {
        if (this != &other) {
            delete[] data_; // 释放当前已有的资源
            data_ = other.data_;
            size_ = other.size_;
            other.data_ = nullptr;
            other.size_ = 0;
        }
        return *this;
    }

    void process() { if(data_) std::cout << "Processing buffer...\n"; }
};

方案二:利用智能指针和自定义删除器(现代 C++ 推荐)

如果你正在开发部署相关的 C++ 应用程序,利用 std::unique_ptr 配合自定义删除器 (Custom Deleter) 是最安全、代码量最少且最高效的方式。这非常适合管理推理引擎的 Context 或 Stream。

复制代码
#include <memory>
#include <iostream>

// 模拟一个第三方 C API (如 OpenVINO 或 TensorRT)
struct MLHandle { int id; };
void close_handle(MLHandle* h) { 
    std::cout << "Closing Handle " << h->id << "\n";
    delete h; 
}

class InferenceManager {
private:
    // 使用自定义删除器管理 C 风格指针
    struct Deleter {
        void operator()(MLHandle* h) const { close_handle(h); }
    };
    
    std::unique_ptr<MLHandle, Deleter> handle_;

public:
    InferenceManager(int id) : handle_(new MLHandle{id}) {}
    
    // 默认支持移动语义,但自动禁止拷贝
    InferenceManager(InferenceManager&&) = default;
    InferenceManager& operator=(InferenceManager&&) = default;

    void run() { std::cout << "Running inference on " << handle_->id << "\n"; }
};

高效资源管理的 3 个关键建议

  1. 优先使用移动语义而非拷贝 :在你处理 3D 点云这种大数据量任务时,拷贝一个包含数百万个点的对象是非常低效的。通过 std::move 转移所有权几乎是零开销的。

  2. 避免原始指针 :尽量不要在业务代码中手动调用 delete。利用 RAII 包装器,即便在程序发生异常(Exception)时,资源也能被正确释放。

  3. 零开销抽象 (Zero-overhead):上述两种方案在编译后,其性能与手动管理几乎完全一致,但却能极大地降低 C++ 应用程序中的内存泄漏风险。

相关推荐
逻辑君2 小时前
Research in Brain-inspired Computing [7]-带关节小人(3个)推箱的类意识报告
c++·人工智能·神经网络·机器学习
txinyu的博客2 小时前
解析muduo源码之 HttpResponse.h & HttpResponse.cc
c++
小白学习记录555553 小时前
vs2019无法自动补全QT代码
c++
小糯米6013 小时前
C++ 单调栈原理与模板
开发语言·c++·算法
XZXZZX3 小时前
ATCODER ABC 450 C题解
c++·算法·ccf csp
像素猎人3 小时前
差分数组【自用笔记】【c++】
c++·笔记·算法
kyle~3 小时前
ROS2 Control
c++·嵌入式硬件·机器人·ros2
xiaoye-duck4 小时前
《算法题讲解指南:优选算法-哈希表》--58.存在重复元素I,59.存在重复元素II,60.字母异位词分组
数据结构·c++·哈希算法
hetao17338374 小时前
2026-03-26 ZYZ28-CSP-XiaoMao Round 2 hetao1733837 的 record
c++·算法