跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++

C++11 详解:列表初始化与右值引用移动语义

综述由AI生成C++11 核心特性涵盖列表初始化与右值引用。列表初始化统一了对象构造语法,支持内置及自定义类型,结合 initializer_list 简化容器操作。右值引用区分左值与临时对象,可延长临时变量生命周期。移动语义通过转移资源所有权替代深拷贝,显著提升性能。理解这些机制有助于编写更高效的 C++ 程序。

邪神洛基发布于 2026/3/21更新于 2026/6/1317 浏览
C++11 详解:列表初始化与右值引用移动语义
C++11 发展背景

C++11 是 C++ 标准演进中的里程碑,从 C++98 到 C++11 间隔了 8 年。它标准化了许多既有实践,引入了大量新特性,极大地提升了开发效率和代码安全性。在此之后,C++ 更新周期稳定为每三年一次。

列表初始化
C++98 的传统方式

在 C++98 中,数组和结构体可以使用 {} 进行初始化,但语法较为分散。

struct Point { int _x; int _y; };
int main() {
    int array1[] = { 1, 2, 3, 4, 5 };
    int array2[5] = { 0 };
    Point p = { 1, 2 };
    return 0;
}
C++11 的统一初始化

C++11 引入列表初始化(List Initialization),试图实现一切对象皆可用 {} 初始化。这不仅支持内置类型,也支持自定义类型。对于自定义类型,本质上是利用构造函数进行隐式转换,编译器优化后通常变为直接构造,避免了不必要的临时对象开销。

值得注意的是,{} 初始化可以省略 = 号,这在容器操作中尤为方便。

#include<iostream>
#include<vector>
using namespace std;

struct Point { int _x; int _y; };

class Date {
public:
    Date(int year = 1, int month = 1, int day = 1) :_year(year), _month(month), _day(day) {
        cout << "Date(int year, int month, int day)" << endl;
    }
    Date(const Date& d) :_year(d._year), _month(d._month), _day(d._day) {
        cout << "Date(const Date& d)" << endl;
    }
private:
    int _year; int _month; int _day;
};

int main() {
    // 内置类型
    int x1 = { 2 };
    int x2{ 2 };

    // 自定义类型
    Date d1 = { 2025, 1, 1 };      // 直接构造,未调用拷贝构造
    Date d6{ 2024, 7, 25 };       // 省略=号
    const Date& d7{ 2024, 7, 25 }; // 引用延长生命周期

    vector<Date> v;
    // 相比有名对象或匿名对象传参,{} 初始化更简洁高效
    v.push_back({ 2025, 1, 1 });
    return 0;
}
std::initializer_list

虽然列表初始化很方便,但在处理容器时,如果不想写多个重载构造函数,std::initializer_list 提供了更好的支持。它本质上是一个轻量级的数组视图,内部维护指向数据开始和结束的指针。

STL 容器普遍增加了接受 initializer_list 的构造函数和赋值运算符,这使得我们可以用任意数量的值来初始化容器。

#include<iostream>
#include<vector>
#include<map>
using namespace std;

int main() {
    // initializer_list 本质是底层开一个数组
    std::initializer_list<int> mylist = { 10, 20, 30 };
    cout << sizeof(mylist) << endl; 

    // 容器初始化
    vector<int> v1({ 1, 2, 3, 4, 5 });
    vector<int> v2 = { 1, 2, 3, 4, 5 };
    
    // map 的 key-value 对初始化
    map<string, string> dict = { {"sort", "排序"}, {"string", "字符串"} };
    
    // 赋值操作也支持
    v1 = { 10, 20, 30, 40, 50 };
    return 0;
}

总结:任意类型都支持列表初始化,自定义类型依赖构造函数。initializer_list 主要是为 STL 容器设计的,让容器支持变长参数初始化,本质是将列表值传给容器的对应构造函数。

右值引用和移动语义

C++98 只有左值引用,C++11 新增了右值引用。无论是左值还是右值引用,本质上都是给对象取别名,不开辟新的内存空间。

左值和右值的区别
  • 左值 (lvalue):表示数据的表达式,有持久状态,可获取地址,能出现在赋值符号左边。现代定义中,lvalue 意为 locator value,即存储在内存中有明确地址的对象。
  • 右值 (rvalue):表示数据的表达式,通常是字面量常量或临时对象,不能取地址,只能出现在赋值符号右边。rvalue 意为 read value,提供数据值但不可寻址。
#include<iostream>
using namespace std;

int main() {
    int b = 1;
    int* p = new int(0);
    
    // 左值:可取地址
    cout << &b << endl;
    cout << &p << endl;

    // 右值:不可取地址
    // cout << &10 << endl; // 编译错误
    double x = 1.1, y = 2.2;
    // cout << &(x + y) << endl; // 编译错误
    return 0;
}
左值引用和右值引用
  • Type& r1 = x; 是左值引用,绑定左值。
  • Type&& rr1 = y; 是右值引用,绑定右值。
  • 左值引用不能直接绑定右值,但 const 左值引用可以。
  • 右值引用不能直接绑定左值,但可以通过 std::move 将左值强转为右值。

变量表达式本身属性是左值,这意味着即使一个右值被右值引用绑定后,该引用变量在表达式中仍表现为左值属性。这一点在函数重载匹配时非常关键。

#include<iostream>
using namespace std;

void f(int& x) { cout << "左值引用" << endl; }
void f(const int& x) { cout << "const 左值引用" << endl; }
void f(int&& x) { cout << "右值引用" << endl; }

int main() {
    int i = 1;
    const int ci = 2;
    
    f(i);           // 调用 f(int&)
    f(ci);          // 调用 f(const int&)
    f(3);           // 调用 f(int&&)
    f(std::move(i)); // 调用 f(int&&)

    int&& x = 1;
    f(x);           // x 是左值属性,调用 f(int&)
    f(std::move(x)); // 调用 f(int&&)
    return 0;
}
引用延长生命周期

右值引用可用于为临时对象延长生命周期。const 左值引用也能延长临时对象生存期,但对象不可修改。

#include<iostream>
#include<string>
using namespace std;

int main() {
    std::string s1 = "Test";
    
    // const 左值引用延长生命周期
    const std::string& r2 = s1 + s1;
    // r2 += "Test"; // 错误:const 不可修改

    // 右值引用延长生命周期且可修改
    std::string&& r3 = s1 + s1;
    r3 += "Test";   // OK
    cout << r3 << '\n';
    return 0;
}
移动构造和移动赋值

对于包含动态资源(如深拷贝的 string、vector)的类,移动构造和移动赋值至关重要。它们通过'窃取'右值对象的资源指针,而非复制数据,从而大幅提升性能。

  • 移动构造函数:第一个参数为右值引用,其他参数需有默认值。
  • 移动赋值运算符:第一个参数为右值引用,与拷贝赋值构成重载。
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;

namespace lrq {
class string {
public:
    typedef char* iterator;
    iterator begin() { return _str; }
    iterator end() { return _str + _size; }
    
    string(const char* str) :_size(strlen(str)), _capacity(_size) {
        cout << "string(char* str)-构造" << endl;
        _str = new char[_capacity + 1];
        strcpy(_str, str);
    }

    void swap(string& s) {
        ::swap(_str, s._str);
        ::swap(_size, s._size);
        ::swap(_capacity, s._capacity);
    }

    // 拷贝构造
    string(const string& s) :_str(nullptr) {
        cout << "string(const string& s) -- 拷贝构造" << endl;
        reserve(s._capacity);
        for (auto ch : s) push_back(ch);
    }

    // 移动构造
    string(string&& s) {
        cout << "string(string&& s) -- 移动构造" << endl;
        swap(s);
    }

    // 拷贝赋值
    string& operator=(const string& s) {
        cout << "string& operator=(const string& s) -- 拷贝赋值" << endl;
        if (this != &s) {
            delete[] _str;
            _str = nullptr;
            _size = 0;
            reserve(s._capacity);
            for (auto ch : s) push_back(ch);
        }
        return *this;
    }

    // 移动赋值
    string& operator=(string&& s) {
        cout << "string& operator=(string&& s) -- 移动赋值" << endl;
        swap(s);
        return *this;
    }

    ~string() {
        cout << "~string() -- 析构" << endl;
        delete[] _str;
        _str = nullptr;
    }

    void reserve(size_t n) {
        if (n > _capacity) {
            char* tmp = new char[n + 1];
            if (_str) strcpy(tmp, _str);
            delete[] _str;
            _str = tmp;
            _capacity = n;
        }
    }

    void push_back(char ch) {
        if (_size >= _capacity) {
            size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
            reserve(newcapacity);
        }
        _str[_size] = ch;
        ++_size;
        _str[_size] = '\0';
    }

    const char* c_str() const { return _str; }
    size_t size() const { return _size; }

private:
    char* _str = nullptr;
    size_t _size = 0;
    size_t _capacity = 0;
};
}

int main() {
    lrq::string s1("xxxxx");
    lrq::string s2 = s1;              // 拷贝构造
    lrq::string s3 = lrq::string("yyyyy"); // 移动构造(优化后直接构造)
    return 0;
}
解决传值返回问题

在 C++98 中,返回局部对象会导致拷贝开销。C++11 的移动语义配合编译器优化(如 RVO/NRVO),使得返回临时对象变得高效。

当存在移动构造/赋值时,编译器会优先选择移动操作而非拷贝操作,避免深拷贝带来的性能损耗。在 Release 模式下,编译器甚至可能直接将返回值构造到目标位置,完全消除中间对象。

注意:右值引用变量在用于表达式时属性是左值,这一设计看似奇怪,实则是为了允许我们在需要时再次使用这些对象,同时保持其作为右值的语义特征。

目录

  1. C++11 发展背景
  2. 列表初始化
  3. C++98 的传统方式
  4. C++11 的统一初始化
  5. std::initializer_list
  6. 右值引用和移动语义
  7. 左值和右值的区别
  8. 左值引用和右值引用
  9. 引用延长生命周期
  10. 移动构造和移动赋值
  11. 解决传值返回问题
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Ansible 批量部署 Nginx 实战指南
  • Python 基础语法入门:常量、变量与运算符
  • So-VITS-SVC 语音合成与 Stable Diffusion 文生图模型搭建指南
  • 大模型算法岗常见面试题汇总与解析
  • STL 转 STEP 格式转换工具 stltostp 使用指南
  • FPGA中扇出数目是什么意思
  • 基于 MCP Server - Figma AI Bridge 自动生成前端代码
  • Visual C++ 运行库安装与 DLL 缺失问题排查指南
  • 大模型训练全流程指南:预训练与指令微调
  • FPGA小白学习日志一:LED的点亮
  • Stable Diffusion 3.5-FP8 模型是否支持 WebGPU 加速
  • Python 自动化办公与网络爬虫实战应用场景解析
  • 普通产品经理转型 AI 产品经理:必备准备与技能提升
  • Java 接入 AI 大模型个人实践:多轮对话与流式输出实现
  • 生产级 AI Agent 框架设计与实战
  • .NET 集成 GoView 低代码可视化大屏完整案例详解
  • GitHub Copilot 在 VS Code 中的代理功能使用指南
  • 算法学习入门指南:数据结构与核心算法实战
  • Ubuntu 24.04 离线部署 Ollama 及模型导入实战指南
  • 攻防世界 Web 题解:Lottery 与 ics-05 漏洞分析

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online