【C++11新章】列表初始化详解

🌈个人主页:聆风吟_

🔥系列专栏:C++11新章

🔖少年有梦不应止于心动,更要付诸行动。


文章目录

  • [一、背景:C++98传统的 {}](#一、背景:C++98传统的 {})
  • 二、什么是列表初始化?
  • 三、基础用法示例
    • [3.1 基础变量初始化](#3.1 基础变量初始化)
    • [3.2 数组初始化](#3.2 数组初始化)
    • [3.3 动态分配内存](#3.3 动态分配内存)
  • 四、进阶用法示例
    • [4.1 结构体初始化](#4.1 结构体初始化)
    • [4.2 类初始化](#4.2 类初始化)
    • [4.3 STL 容器初始化](#4.3 STL 容器初始化)
  • [六、列表初始化的 4 大好处](#六、列表初始化的 4 大好处)
  • 📝全文总结

一、背景:C++98传统的 {}

C++98中一般数组和结构体可以用 {} 进行初始化。

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

struct Point
{
        int _x;
        int _y;
};

int main()
{
        // 变量:=
        int x = 10;
        // 数组:{}
        int array1[] = { 1, 2, 3, 4, 5 };
        int array2[5] = { 0 };
        // 结构体:{}
        Point p = { 1, 2 };

        return 0;
}

二、什么是列表初始化?

在 C++11 之前,初始化方式特别乱:

  • 变量用 =

  • 数组用 {}

  • 对象用 ()

  • 容器没法直接批量赋值

C++11 直接统一:不管什么类型,全都能用 {} 初始化 ,这就叫列表初始化

语法格式(两种写法完全等价,都可以用):

cpp 复制代码
类型 变量名{值1, 值2, ...};   // 推荐写法
类型 变量名 = {值1, 值2, ...};

📌小贴士: {}初始化过程中可以省略 =


三、基础用法示例

3.1 基础变量初始化

内置类型(int、double、bool 等)直接用 {} 赋值,语法更统一。

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

int main()
{
    // C++98 传统写法
    int a = 10;
    double b = 3.14;

    // C++11 列表初始化
    int c{ 10 };        // 等价 int c = 10;
    double d{ 3.14 };   // 等价 double d = 3.14;
    bool e{ true };     // 等价 bool e = true;

    cout << "c = " << c << endl;
    cout << "d = " << d << endl;
    cout << "e = " << e << endl;

    return 0;
}

输出结果:

Plain 复制代码
c = 10
d = 3.14
e = 1

3.2 数组初始化

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

int main()
{
    // C++98
    int arr1[3] = { 1, 2, 3 };

    // C++11 列表初始化(两种写法等价)
    int arr2[3] = { 1, 2, 3 };
    int arr3[]{ 1, 2, 3 };    // 自动推导长度为3
    int arr4[5]{ 1, 2, 3 };   // 未赋值元素自动初始化为0

    return 0;
}

3.3 动态分配内存

C++11 支持用 {} 初始化动态分配的对象:

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

class Person
{
public:
    Person(int a, string n) : age(a), name(n) {}

private:
    int age;
    string name;
};

int main()
{
    // 动态int数组,初始化为{1,2,3}
    int* arr = new int[3] {1, 2, 3};

    // 动态对象初始化
    Person* p = new Person{ 25, "王五" };

    return 0;
}

四、进阶用法示例

4.1 结构体初始化

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

struct Point
{
    int x;
    int y;
    int z;
};

int main()
{
    // 列表初始化结构体
    
    // 1. 完整初始化(给所有成员赋值)
    Point d1 = { 2026, 1, 1 };  // 传统写法
    Point d2{ 2026, 1, 1 };     // C++11 简写(推荐)

    // 2. 部分初始化(没写的自动 = 0)
    Point d3{ 2026 };     // year=2026, month=0, day=0
    Point d4{ 2026, 1 };  // year=2026, month=1, day=0

    // 3. 零初始化(全部 = 0)
    Point d5{};  // year=0, month=0, day=0

    return 0;
}

4.2 类初始化

列表初始化会自动调用匹配的构造函数

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

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;
};

(1)C++98 传统初始化

cpp 复制代码
// 传统圆括号初始化:直接调用构造函数
int main()
{
    Date d0(2025, 1, 1);
    
    return 0;
}

输出:

Plain 复制代码
Date(int year, int month, int day) ------> 构造函数

(2)C++11 列表初始化

cpp 复制代码
// 写法1:赋值形式的列表初始化
// 理论:{2026,1,1} → 构造临时对象 → 拷贝构造d1
// 编译器优化:直接构造d1,不会调用拷贝构造
Date d1 = { 2026, 1, 1 };

// 写法2:直接列表初始化(推荐)
// 最简洁,直接调用构造函数
Date d2{ 2026, 1, 1 };

输出:

cpp 复制代码
Date(int year, int month, int day) ------> 构造函数
Date(int year, int month, int day) ------> 构造函数

(3)隐式类型转换

cpp 复制代码
int main()
{
    // 因为构造是全缺省,1个参数也能匹配
    Date d3 = { 2025 };  // 列表初始化
    Date d4 = 2025;      // C++98 隐式类型转换
    
    return 0;
}

输出:

cpp 复制代码
Date(int year, int month, int day) ------> 构造函数
Date(int year, int month, int day) ------> 构造函数

(4)使用场景

cpp 复制代码
int main()
{
    vector<Date> v;

    // 写法1:隐式转换构造
    v.push_back(2026);

    // 写法2:列表初始化构造(最常用)
    v.push_back({ 2026, 1, 1 });

    // 错误写法:
    // v.push_back(2026, 1, 1); // 报错!push_back 只能传1个参数
    return 0;
}

讲解:

  1. push_back(2026):2026 隐式转换成 Date 对象,先构造,再拷贝进容器

  2. push_back({2026,1,1}){2026,1,1} 直接构造 Date 临时对象,再拷贝进容器

  3. 为什么不能写 push_back (2026,1,1)

    • push_back 只能接收 1 个参数

    • 三个 int 是 3 个参数,语法错误

    • 必须用 {} 打包成 一个对象 才能传入

输出:

cpp 复制代码
Date(int year, int month, int day) ------> 构造函数
Date(const Date& d) ------> 拷贝构造函数
Date(int year, int month, int day) ------> 构造函数
Date(const Date& d) ------> 拷贝构造函数

4.3 STL 容器初始化

这是 C++11 最爽的地方,vector、map、string 等容器都能直接赋值

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

int main()
{
    // vector 直接批量赋值
    vector<int> v{1, 2, 3, 4, 5};  

    // string 初始化
    string s{"hello world"};  

    // map 键值对直接初始化
    map<int, string> mp{{1, "one"}, {2, "two"}};  

    return 0;
}

六、列表初始化的 4 大好处

(1)统一所有初始化写法

不管是基础变量、数组、结构体、类、STL容器全部只用 {} 一种写法 ,彻底告别 =(){} 混用的混乱规则。

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

// 旧版 C++:初始化语法五花八门,极易记混
int a = 10;                  // 等号
int arr[] = { 1,2,3 };       // 数组
std::vector<int> v(2, 5);    // 构造函数()
struct Point { int x, y; };
Point p = { 1, 2 };          // 结构体

// 现代 C++:全部统一用 {},一套规则走天下
int a{ 10 };                 // 变量
int arr[]{ 1,2,3 };          // 数组
std::vector<int> v{ 2,5 };   // 容器
Point p{ 1,2 };              // 结构体
std::string s{ "hello" };    // 字符串

总结:列表初始化 {} 语法,统一所有类型初始化,代码风格更整洁,学习/记忆成本大幅降低。

(2)防止「窄化转换」,编译期拦截类型风险

禁止不安全的隐式缩窄转换 (比如 double → intlong → short),编译器直接报错,从根源避免数值丢失、精度错误。

📌小贴士

窄化转换:把大范围类型偷偷转成小范围(比如 double → int,long → char)。

cpp 复制代码
// 1. 传统初始化:隐式缩窄转换,编译器不报错(埋坑!)
double pi = 3.1415;
int x = pi;        // 合法!double 偷偷转 int,精度丢失
char c = 1000;     // 合法!数值溢出,结果不可预期

// 2. 列表初始化:严格禁止缩窄转换,直接编译报错(安全!)
int x{pi};         // 报错:无法从 double 缩窄为 int
char c{1000};      // 报错:1000 超出 char 范围
float f{1.2345};   // 报错:double 不能缩窄为 float

总结:列表初始化 = 类型安全防火墙,所有不安全的隐式类型转换,都会在编译期被拦截,程序更健壮。

(3)容器直接批量赋值,告别繁琐 push_back

vector/map/list 等所有 STL 容器,直接用 {} 批量初始化/赋值 ,一行代码替代 N 行 push_back,代码极简直观。

cpp 复制代码
#include <vector>
#include <map>
#include <iostream>

// 旧版:反复 push_back,代码冗长
std::vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);

// 列表初始化:一行搞定,清晰优雅
std::vector<int> v2{1,2,3,4};

// map 同样支持,键值对直接批量写
std::map<std::string, int> score{
    {"张三", 90},
    {"李四", 85},
    {"王五", 95}
};

总结:批量初始化容器,代码量锐减 80%,可读性、维护性直接拉满。

(4)空列表 {} 默认初始化,杜绝野值

对任意类型使用 {}编译器会自动执行「值初始化」

  • 基础类型(int/double/指针)→ 自动清零

  • 自定义类型 → 调用默认构造

彻底杜绝未初始化变量导致的野值、崩溃、随机bug

cpp 复制代码
// 1. 传统定义:不初始化 = 随机野值(高危!)
int a;          // 未初始化,值是内存垃圾(随机数)
double d;       // 随机垃圾值
int* ptr;       // 野指针,访问直接崩溃

// 2. 空列表 {}:强制默认初始化,全部安全清零
int a{};        // = 0
double d{};     // = 0.0
int* ptr{};     // = nullptr(空指针,安全)
std::vector<int> v{};  // 空容器

// 结构体/类同样适用
struct Point { int x,y; };
Point p{};      // x=0, y=0

总结:{} = 安全初始化兜底,再也不用担心忘记赋值导致的诡异bug。


📝全文总结

C++11列表初始化({} 是对C++98繁杂初始化语法的大一统升级 ,它用一套极简、安全、统一的规则,彻底解决了传统初始化方式混乱、易错、不安全的痛点,是现代C++开发中首选、推荐、必备的初始化方式。它的核心价值可以浓缩为4句话:

  1. 语法大一统基础变量、数组、结构体、类对象、STL容器 ,所有类型统一用{}初始化,代码风格极致简洁统一;
  2. 类型更安全 :编译期严格禁止窄化转换(如double转int、大数转char),从源头拦截类型溢出、精度丢失的风险;
  3. 编码更高效 :STL容器(vector/map/list等)支持直接批量赋值,一行代码替代多行push_back,大幅简化容器初始化;
  4. 使用更稳健 :空列表{}会自动对所有类型执行值初始化(基础类型清零、指针置nullptr、自定义类型调默认构造),彻底杜绝野值、野指针导致的程序bug。

简单来说:现代C++开发,初始化就用{},简洁、安全、不出错!

今天的干货分享到这里就结束啦!如果觉得文章还可以的话,希望能给个三连支持一下,聆风吟的主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是作者前进的最大动力!

相关推荐
alwaysrun1 小时前
C++之灵活易用的YAML解析库yaml-cpp
c++·后端·程序员
闪电悠米1 小时前
黑马点评-秒杀优化-04_lua_and_db_fallback
服务器·开发语言·网络·数据库·缓存·junit·lua
Shadow(⊙o⊙)1 小时前
进程间通信0.0-pipe()匿名管道,详细分析进程池调度队列执行逻辑,进程池模拟实现。
linux·运维·服务器·开发语言·c++
lcj25111 小时前
【list】【手撕 STL】List 容器全解析!迭代器 / 增删改查 / 去重排序,面试必背的核心考点!
c++·面试·list
指尖的爷1 小时前
C++头文件的作用
开发语言·c++
keykey6.1 小时前
反向传播与梯度下降:神经网络如何学习
开发语言·人工智能·深度学习·机器学习
Jun6261 小时前
QT(5)-第三方日志系统
开发语言·数据库·qt
冰暮流星2 小时前
javascript建立对象之构造函数
开发语言·javascript·ecmascript
keykey6.2 小时前
PyTorch 入门实战:从张量到训练循环
开发语言·人工智能·深度学习·机器学习