一.C++11:统一列表初始化 + std::initializer_list 超详细精讲

系列文章目录

提示:这里是系列文章的专栏

并不喜欢吃鱼的C++专栏


提示:以下是文章目录哦!

文章目录

目录

系列文章目录

文章目录

前言

[1. C++98 初始化的痛点(对比引出 C++11)](#1. C++98 初始化的痛点(对比引出 C++11))

[2. C++11 统一列表初始化 {}(全场景代码 + 逐行注释)](#2. C++11 统一列表初始化 {}(全场景代码 + 逐行注释))

[2.1 内置类型初始化](#2.1 内置类型初始化)

[2.2 结构体 / 类初始化](#2.2 结构体 / 类初始化)

[2.3 STL 容器初始化](#2.3 STL 容器初始化)

[2.4 new + {} 动态内存初始化](#2.4 new + {} 动态内存初始化)

[2.5 直接列表初始化 {} / 拷贝列表初始化 ={}](#2.5 直接列表初始化 {} / 拷贝列表初始化 ={})

[案例 1:无 explicit 的多参构造(C++11)](#案例 1:无 explicit 的多参构造(C++11))

[案例 2:explicit 修饰多参数构造(重点)](#案例 2:explicit 修饰多参数构造(重点))

分版本规则细化(上课总结)

[1.C++98 旧标准](#1.C++98 旧标准)

[2.C++11 及以后新标准(考点)](#2.C++11 及以后新标准(考点))

[3. 列表初始化的强大优势:禁止窄化转换](#3. 列表初始化的强大优势:禁止窄化转换)

[4. std::initializer_list({} 批量初始化的底层)](#4. std::initializer_list({} 批量初始化的底层))

[4.1 基本用法(逐行注释)](#4.1 基本用法(逐行注释))

5.本章总结


前言

提示:这里可以添加本文要记录的大概内容:

我们已经完整结束 C++ 数据结构(vector/list/map/set 等 STL 容器)全部内容,在容器练习中大家大概率遇到一个疑惑:为什么vector<int> v={1,2,3,4};这种花括号直接初始化写法,C++98 不支持、C++11 却可以直接使用?这正是我们开启 C++11 新特性学习的第一个切入点 ------统一列表初始化(List Initialization)

C++98/03 历经十余年使用,初始化语法长期处于割裂状态:数组、普通结构体才能用{}初始化,内置类型靠=/()、自定义类依赖构造函数()、STL 容器初始化需要逐个 push_back,语法杂乱、写法不统一还容易出现窄化转换隐患。2011 年 ISO 正式落地 C++11 标准,首当其冲解决的就是初始化混乱问题:推出全类型通用的 {} 列表初始化 ,再通过std::initializer_list支撑 STL 容器批量初始化,彻底实现「万物皆可大括号初始化」

作为 C++11 最基础、日常编码最高频的特性,列表初始化是后续学习右值引用、移动语义、可变参数模板、emplace 系列接口的前置铺垫。本文会从C++98 初始化痛点→C++11 列表初始化语法→initializer_list 原理→容器实战→手写模拟容器层层拆解,结合课件全部示例代码,打通从基础变量到 STL 容器的初始化全链路,帮大家彻底吃透 C++11 初始化核心


提示:以下是本篇文章正文内容

1. C++98 初始化的痛点(对比引出 C++11)

弊端总结:

  • 语法不统一
  • 类不能直接 {} 初始化
  • 容器不能批量初始化

2. C++11 统一列表初始化 {}(全场景代码 + 逐行注释)

C++11 规则:任何对象都可以用 {} 初始化,= 可写可不写

但是以上这句话,有些不太准确,我们在2.5来仔细讲讲

2.1 内置类型初始化

运行结果如下:

所以以上初始化的方法我们直接记住写法2就行了


2.2 结构体 / 类初始化

cpp 复制代码
#include <iostream>
using namespace std;

// 结构体(聚合类型)
struct Point
{
    int x;
    int y;
};

// 类
class Date
{
public:
    // 构造函数
    Date(int year, int month, int day)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    // 打印函数
    void Print()
    {
        cout << _year << "-" << _month << "-" << _day << endl;
    }

private:
    int _year;
    int _month;
    int _day;
};

int main()
{
    // 1. 结构体 C++11 列表初始化
    Point p{ 10, 20 };       // 直接 {},不需要 =
    cout << p.x << " " << p.y << endl;

    // 2. 类 C++11 列表初始化(C++98 做不到!)
    Date d1{ 2025, 1, 1 };   // 直接调用构造函数
    d1.Print();

    Date d2 = { 2024, 12, 25 }; // 加 = 也可以
    d2.Print();

    return 0;
}

运行结果如下:

可见不管是内置类型还是自定义类型都是可以用**"{}"**来进行初始化的


2.3 STL 容器初始化

cpp 复制代码
#include <iostream>
#include <vector>
#include <map>
#include <string>
using namespace std;

int main()
{
    // vector 直接用 {} 批量初始化
    vector<int> v{ 1,2,3,4,5 };

    // 遍历打印
    for (auto e : v)
    {
        cout << e << " ";
    }
    cout << endl;

    // map 嵌套 {} 初始化
    map<string, string> dict{
        {"apple", "苹果"},
        {"book", "书"},
        {"student", "学生"}
    };

    // 遍历 map
    for (auto& kv : dict)
    {
        cout << kv.first << " : " << kv.second << endl;
    }

    return 0;
}

运行结果如下:

这里顺便讲讲map 的 {} 初始化 :

第一步:回忆 map 到底存的是什么?

map<string, string> 里面存的不是两个独立字符串,而是:

pair 是键值对结构体:

所以:map 存的是一堆 pair,map 的 {} 初始化 = 批量插入 N 个 pair

第二步:一层一层拆大括号

拆第一层:最外层 {...}

这是一个 initializer_list< pair<string,string> > → 传给 map 的构造函数

拆第二层:里面每一个 {...}

发生隐式类型转换:

所以整段代码:

等价于:


2.4 new + {} 动态内存初始化

运行结果如下(注意一下写法):


2.5 直接列表初始化 {} / 拷贝列表初始化 ={}

拷贝列表不能用 explicit 构造函数隐式转换,直接列表可以,=决定初始化种类,explicit 只拦截带 = 的拷贝列表初始化

案例 2:explicit 修饰多参数构造(重点)

explicit关键字针对多参数构造也是一样的,进制隐式类型转换

案例 1:无 explicit 的多参构造(C++11)

案例 2:explicit 修饰多参数构造(重点)

分版本规则细化(上课总结)

1.C++98 旧标准
  • explicit仅对单参数构造生效
  • 多参数构造:Test t={1,2};直接编译报错,天生不能隐式转换explicit Test(int,int)写了等于没写
2.C++11 及以后新标准(考点)
  1. Test t{1,2}(不带 =,直接列表):无论是否 explicit,全部合法,直接调用构造
  2. Test t={1,2}(带 =,拷贝列表):explicit 修饰后报错,不加 explicit 则允许隐式转换
  3. Test t(1,2)(圆括号):永远显式调用,explicit 不影响

3. 列表初始化的强大优势:禁止窄化转换

窄化转换:把大范围类型赋值给小范围类型(double → int,int → char 等)

C++98 允许,会隐式丢失数据; C++11 {} 初始化直接禁止,编译报错!更安全!


4. std::initializer_list({} 批量初始化的底层)

4.1 基本用法(逐行注释)

5.本章总结

  1. C++11 用 {} 统一所有类型初始化,语法简洁、安全
  2. {}禁止窄化转换,避免隐式数据丢失
  3. std::initializer_list容器批量初始化的底层支撑
  4. 所有容器都提供了 initializer_list 版本构造 + 赋值
  5. 自己写类 / 容器时,加一个 initializer_list 构造,就能支持 {} 初始化
相关推荐
CHHH_HHH1 小时前
【C++】二叉搜索树全面升级,深度剖析AVL树
开发语言·数据结构·c++·算法·stl
奋斗的小方1 小时前
Java基础篇09(2):项目实战之基于swing的石头迷阵
java·开发语言
做cv的小昊1 小时前
计算机图形学:【Games101】学习笔记06——几何(曲线和曲面、网格处理)、阴影图
c++·笔记·学习·游戏·图形渲染·几何学·光照贴图
Evand J1 小时前
【代码介绍】自适应R的AEKF(自适应扩展卡尔曼滤波)和经典EKF比较,MATLAB例程|三维非线性系统
开发语言·matlab·ekf·自适应·自适应滤波
WBluuue2 小时前
数据结构与算法:树上启发式合并
数据结构·c++·算法·启发式算法
学无止境_永不停歇2 小时前
从零手写高性能C++ TCP 服务器框架(十一) --- Connection实现
linux·服务器·网络·c++
努力的章鱼bro2 小时前
CUDA编程入门
c++·人工智能·cuda
雪的季节2 小时前
1 个网络线程 + 3 个数据处理线程(完全隔离)
开发语言
风筝在晴天搁浅2 小时前
快手 CodeTop LeetCode 227.基本计算器Ⅱ
java·开发语言