智享教程网
白蓝主题五 · 清爽阅读
首页  > 日常经验

C++标准右值引用:别再让临时对象白白浪费内存了

C++ 时你有没有遇到过这种事:函数返回一个大对象,调用方又立刻拿它初始化另一个对象,结果编译器默默拷贝了一整份——几百 KB 的 vector、上万行的 string,全被复制了一遍。性能掉得莫名其妙,一查发现是拷贝构造在背锅。

右值引用不是语法糖,是救命稻草

从 C++11 开始,&& 不再是逻辑与的加强版,而是右值引用的专属符号。它能精准绑定到临时对象(比如函数返回值、字面量、表达式结果),让编译器知道:“这个东西马上就要没了,你可以直接‘搬走’它的资源,不用复制。”

看个最直白的例子:

std::vector<int> create_big_vec() {
    return std::vector<int>(100000, 42); // 返回一个临时 vector
}

// 旧写法(C++98):深拷贝一次
std::vector<int> v1 = create_big_vec();

// 新写法(C++11+):自动触发移动构造,只交换内部指针
std::vector<int> v2 = create_big_vec();

这里 v2 构造时调用的是 vector 的移动构造函数,而不是拷贝构造函数——底层只是把原临时对象的 data_ 指针和 size_ 值“偷”过来,再把原对象清空。毫秒级完成,零内存分配。

自己写类?加个移动构造就立竿见影

假设你有个管理动态数组的类:

class MyString {
    char* data_;
    size_t len_;
public:
    MyString(const char* s) : len_(strlen(s)) {
        data_ = new char[len_ + 1];
        strcpy(data_, s);
    }

    // 移动构造函数:接收右值引用
    MyString(MyString&& other) noexcept
        : data_(other.data_), len_(other.len_) {
        other.data_ = nullptr; // 把对方掏空
        other.len_ = 0;
    }

    // ……析构、拷贝等略
};

有了它,MyString s = get_temp_string(); 就不再 malloc + memcpy,而是指针交接。实测在频繁拼接字符串的场景里,耗时直接砍掉 60% 以上。

小提醒:移动操作最好加 noexcept,否则某些容器(如 std::vector::resize)可能不敢用你的移动函数,悄悄退回到拷贝。