嵌入式AI学习打卡 06 现代C++ 移动语义

56 阅读2分钟

适合谁看:资深的嵌入式系统底层专家。技能栈(C、SoC、Bootloader、驱动、系统移植、DSP优化、Cache调优、功耗分析)是芯片设计的核心和基础,具备从硬件(SoC)底层固件(ROM Bootloader)再到操作系统层(驱动、RTOS)的全栈知识和实战经验,希望向嵌入式场景的AI部署方向拓展,寻求新的发展空间。


image.png

现代C++提供移动语义支持,用以减少不必要的内存拷贝。早期版本的C++无法避免特定场景下的内存拷贝,现在引入了move操作,可以在无需拷贝实现移交资源的所有权移交。你在实现持有一些堆(heap memory)、文件句柄(file handles)这样的资源的类时,可以为它定义一个移动构造函数(move consturctor),供编译器在重载决议选用。

重载决议(Overload Resolution)是编译器在调用函数时,从多个候选函数中选择最匹配的函数的过程。在移动语义中,编译器优先选择移动构造函数移动赋值运算符(如果可用),而不是拷贝操作

class X {
public:
    X();                  // 默认构造
    X(const X&);          // 拷贝构造
    X(X&&);               // 移动构造(右值引用)
};

X a;
X b = a;       // 调用拷贝构造(a 是左值)
X c = X();     // 调用移动构造(X() 是右值)

如果 X 没有定义移动构造函数(即 X(X&&)),那么 X c = X();并不会调用移动构造,而是会回退到拷贝构造或直接优化掉拷贝。

当然了移动构造还是为了减少不必要的拷贝,像下面这样一个类:

class Buffer { 
    int* data; size_t size; 
public: 
    Buffer(size_t size) : size(size), data(new int[size]) {} 
    ~Buffer() { 
        delete[] data; 
    } 
    // 拷贝构造(深拷贝,成本高) 
    Buffer(const Buffer& other) : size(other.size), data(new int[other.size]) {
        std::copy(other.data, other.data + size, data); 
    } 
    // 移动构造(高效,直接“偷”资源) 
    Buffer(Buffer&& other) noexcept : data(other.data), size(other.size) { 
        other.data = nullptr; // 防止原对象析构时释放内存 other.size = 0; 
    } 
 };

用一个长度1024的vector初始化

std::vector<Buffer> vec;
vec.push_back(Buffer(1024));  

如果没有移动构造,会调用拷贝构造(深拷贝 1024 个 int). 有移动构造就会调用移动构造,仅复制指针。

回想智能指针中的unique,禁止拷贝,只能移动所有权, 如果没有移动语义,unique_ptr就无法安全转移所有权

std::unique_ptr<int> p1 = std::make_unique<int>(42);
// std::unique_ptr<int> p2 = p1;  // 错误!不能拷贝
std::unique_ptr<int> p2 = std::move(p1);  // 正确,调用移动构造

什么时候需要移动构造?

场景是否需要移动构造
std::vector/std::string扩容​必须(否则深拷贝)
​资源管理类(unique_ptr、文件句柄)​必须(否则无法转移所有权)
​函数返回局部对象(RVO 可能失效)​推荐(避免深拷贝)
​临时对象传递(如 X())​通常 RVO 优化掉,但移动构造