当前位置: 首页 > news >正文

C++11列表初始化 {}

文章目录

  • C++11列表初始化 {}
    • 一、列表初始化({})的核心特性
    • 二、C++11 列表初始化核心内容(对比 C++98/03 初始化方法)
      • 1. 基本类型与数组初始化
      • 2. 结构体与类初始化
      • 3. 容器初始化(STL)
        • 3.1 序列式容器(`vector`、`list`、`deque`等)
        • 3.2 关联式容器(`map`、`set`、`unordered_map`等)
        • 3.3 嵌套容器初始化
        • 3.4 容器赋值操作
      • 4. 禁止窄化转换
        • 4.1 什么是“窄化转换”?
        • 4.2 列表初始化列表初始化如何禁止窄化转换?
        • 4.3 具体示例:禁止窄化转换的场景
          • (1) 浮点数 → 整数(丢失小数)
          • (2)大整数 → 小整数(可能溢出)
          • (3)高精度浮点数 → 低精度浮点数(丢失精度)
          • (4)例外:安全的转换(非窄化)允许通过
      • 5. `std::initializer_list` -- 初始化列表构造函数
        • 5.1 核心功能与设计目的
        • 5.2 基本特性
        • 5.3 使用场景
          • (1) 作为函数参数
          • (2) 作为类的构造函数参数
          • (3) STL容器中的应用
          • (4)作为返回值
        • 5.4关键注意事项

C++11列表初始化 {}

C++11 引入的列表初始化(List Initialization) 是一种统一的初始化方式,支持使用 {} 对变量、对象、容器等进行初始化,解决了 C++98/03 中初始化语法混乱的问题。其核心特点是兼容多种场景、禁止窄化转换(Narrowing Conversion),并简化了初始化代码。

一、列表初始化({})的核心特性

  1. 统一语法:可用于几乎所有类型的初始化(基本类型、数组、结构体、类、容器等)。
  2. 禁止窄化转换:不允许可能丢失精度的类型转换(如 intfloat、大整数转小整数),编译时会报错。
  3. 支持初始化列表构造:类可通过 std::initializer_list 接收 {} 中的元素,实现批量初始化(如 STL 容器)。

二、C++11 列表初始化核心内容(对比 C++98/03 初始化方法)

1. 基本类型与数组初始化

C++98/03 方式

// 基本类型
int a = 10;
double b(3.14);// 数组
int arr1[3] = {1, 2, 3}; // 只能在定义时初始化,且不能省略大小(部分场景除外)
int arr2[] = {4, 5, 6};  // 需依赖初始化元素数量推导大小

C++11 列表初始化

// 基本类型:更简洁,无需等号或括号
int a{10};         // 等价于 int a = 10;
double b{3.14};    // 等价于 double b(3.14);// 数组:支持省略等号,且语法更统一
int arr1[3]{1, 2, 3}; 
int arr2[]{4, 5, 6};  // 同样支持推导大小

2. 结构体与类初始化

C++98/03 方式

C++98 中,有自定义构造函数的类无法用{}初始化,必须显式调用构造函数;

需依赖默认构造函数或带参构造函数,结构体可直接用 {} 但类需显式调用构 造函数。

struct Point {int x;int y;
};class Student {
private:std::string name;int age;
public:Student(std::string n, int a) : name(n), age(a) {}
};// 初始化
Point p1 = {1, 2};       // 结构体可直接用{}(聚合类型)
Student s1("Tom", 18);   // 类必须调用构造函数

C++11 列表初始化

C++11 允许用{}直接匹配构造函数。

结构体和类均可直接用 {} 初始化,类会自动匹配对应构造函数。

Point p1{1, 2};          // 无需等号,更简洁
Student s1{"Tom", 18};   // 类可直接用{}匹配构造函数,等价于 Student s1("Tom", 18);

3. 容器初始化(STL)

3.1 序列式容器(vectorlistdeque等)

序列式容器存储单一类型的元素,初始化时直接在{}中列出元素即可。

C++98方式(繁琐)

#include <vector>
#include <list>// 初始化vector
std::vector<int> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);// 初始化list
std::list<std::string> lst;
lst.push_back("a");
lst.push_back("b");

C++11列表初始化(简洁)

#include <vector>
#include <list>// 直接用{}传入元素,自动匹配initializer_list构造函数
std::vector<int> vec{1, 2, 3, 4}; // 初始化包含4个int的vector
std::list<std::string> lst{"a", "b", "c"}; // 初始化包含3个string的list
std::deque<double> dq{3.14, 2.718, 1.618}; // 双端队列
3.2 关联式容器(mapsetunordered_map等)

关联式容器存储键值对(map)或单一键(set),初始化时需按容器元素类型传入{}中的元素。

mapunordered_map(键值对)
键值对用{key, value}表示,多个键值对用逗号分隔。

#include <map>
#include <unordered_map>// 初始化map(有序键值对)
std::map<std::string, int> score{{"Alice", 90}, {"Bob", 85}, {"Charlie", 95}
};// 初始化unordered_map(无序键值对)
std::unordered_map<int, std::string> dict{{1, "one"}, {2, "two"}, {3, "three"}
};

setunordered_set(单一键)
直接传入键值即可。

#include <set>
#include <unordered_set>// 初始化set(有序集合)
std::set<int> s{3, 1, 4, 1, 5}; // 自动去重并排序,结果为{1,3,4,5}// 初始化unordered_set(无序集合)
std::unordered_set<std::string> us{"apple", "banana", "cherry"};
3.3 嵌套容器初始化

列表初始化支持嵌套,可直接初始化多维容器(如vector<vector<int>>)。

#include <vector>// 初始化二维vector(矩阵)
std::vector<std::vector<int>> matrix{{1, 2, 3},{4, 5, 6},{7, 8, 9}
};// 初始化vector of map
std::vector<std::map<int, std::string>> vec_of_map{{{1, "a"}, {2, "b"}},{{3, "c"}, {4, "d"}}
};
3.4 容器赋值操作

除了初始化,std::initializer_list还支持对已存在的容器进行赋值。

#include <vector>
#include <set>std::vector<int> vec{1, 2, 3};
vec = {4, 5, 6}; // 赋值新的元素列表,覆盖原有内容std::set<std::string> s;
s = {"x", "y", "z"}; // 赋值后s包含{"x", "y", "z"}

4. 禁止窄化转换

在 C++11 引入的列表初始化({})中,禁止窄化转换(Narrowing Conversion) 是一项重要的安全性特性。它的核心作用是:防止可能导致数据精度丢失或溢出的隐式类型转换,在编译阶段就拦截这类风险,避免运行时错误。

4.1 什么是“窄化转换”?

窄化转换指的是将一种类型的值转换为另一种类型时,可能导致数据信息丢失或溢出的转换。常见场景包括:

  1. 浮点数转整数:如 doubleint(丢失小数部分)。
  2. 大类型转小类型:如 long longint(可能超出范围)。
  3. 高精度转低精度:如 long doublefloat(丢失精度)。

例如:

int a = 3.14;      // 3.14 → 3(丢失小数部分,属于窄化转换)
short b = 100000;  // 100000 超出 short 最大范围(32767),溢出(窄化转换)
float c = 123456789.123;  // 超出 float 精度范围(丢失部分小数,窄化转换)
4.2 列表初始化列表初始化如何禁止窄化转换?

C++11 规定:使用 {} 进行列表初始化时,编译器必须检查并禁止所有窄化转换,若存在则直接编译报错。

与之对比:C++98 中使用 = 或括号的传统初始化允许窄化转换(仅可能触发警告,不会报错)。

4.3 具体示例:禁止窄化转换的场景
(1) 浮点数 → 整数(丢失小数)
// 传统初始化:允许(仅警告)
int a = 3.14;       // 编译通过(a=3)
int b(2.718);       // 编译通过(b=2)// 列表初始化:禁止(编译报错)
int c{3.14};        // 错误:double → int 丢失小数(窄化转换)
int d{2.718};       // 错误:窄化转换
(2)大整数 → 小整数(可能溢出)
// 传统初始化:允许(可能溢出但不报错)
short s1 = 100000;  // 100000 超出 short 范围(32767),编译通过(结果不确定)
char c1 = 300;      // 300 超出 char 范围(-128~127),编译通过(溢出)// 列表初始化:禁止(编译报错)
short s2{100000};   // 错误:整数超出 short 范围(窄化转换)
char c2{300};       // 错误:整数超出 char 范围(窄化转换)
(3)高精度浮点数 → 低精度浮点数(丢失精度)
// 传统初始化:允许(丢失精度但不报错)
float f1 = 123456789.123456789;  // float 精度有限,编译通过(值被截断)// 列表初始化:禁止(编译报错)
float f2{123456789.123456789};   // 错误:long double → float 丢失精度(窄化转换)
(4)例外:安全的转换(非窄化)允许通过

列表初始化并非禁止所有转换,仅禁止可能丢失信息的转换。以下安全转换允许通过:

  • 整数 → 更大的整数(如 intlong long)。
  • 整数 → 浮点数(如 intdouble,浮点数精度足够容纳整数)。
  • 低精度浮点数 → 高精度浮点数(如 floatdouble)。

示例:

// 安全转换:允许通过
int a{100};
long long b{a};          // int → long long(范围更大,非窄化)
double c{100};           // int → double(精度足够,非窄化)
double d{3.14f};         // float → double(精度更高,非窄化)

5. std::initializer_list – 初始化列表构造函数

std::initializer_list 是 C++11 引入的一个轻量级模板类(定义在 <initializer_list> 头文件中),专门用于处理列表初始化(即用 {} 包裹的元素列表)。它为编译器提供了一种统一的方式来处理初始化列表,是 C++11 中列表初始化语法(如容器初始化、变量初始化)的底层实现基础。

5.1 核心功能与设计目的

std::initializer_list 的主要作用是:

  1. 封装初始化列表:将 {a, b, c} 这样的列表元素转换为一个临时的容器对象,供其他代码(如构造函数、函数参数)使用。
  2. 支持统一初始化:让自定义类型(如类、容器)能够像原生数组一样使用 {} 语法初始化。
  3. 简化代码:避免为不同数量的初始化参数编写多个重载函数/构造函数。
5.2 基本特性
  1. 模板类型std::initializer_list<T> 是模板类,其中 T 是列表中元素的类型(如 intstd::string)。
  2. 轻量级:内部仅存储两个指针(或一个指针+长度),指向列表元素的起始和结束位置,不负责内存管理(元素存储在栈上的临时区域)。
  3. 只读访问:元素是常量,不允许修改(std::initializer_list<T> 本质是 const T* 的封装)。
  4. 临时对象std::initializer_list 对象的生命周期与初始化列表相同,通常是表达式结束时销毁。
5.3 使用场景
(1) 作为函数参数

函数可以接收 std::initializer_list<T> 类型的参数,从而支持用 {} 列表传参。

#include <initializer_list>
#include <iostream>// 接收initializer_list的函数
void print(const std::initializer_list<int>& list) {for (auto val : list) { // 遍历列表元素(只读)std::cout << val << " ";}std::cout << std::endl;
}int main() {print({1, 2, 3, 4}); // 直接用{}传参,自动转换为initializer_listprint({10, 20});return 0;
}

输出:

1 2 3 4 
10 20 
(2) 作为类的构造函数参数

类可以定义接收 std::initializer_list<T> 的构造函数,从而支持用 {} 初始化对象(这也是 STL 容器支持列表初始化的原理)。

#include <initializer_list>
#include <vector>class MyContainer {
private:std::vector<int> data;
public:// 接收initializer_list的构造函数MyContainer(std::initializer_list<int> list) {// 将列表元素添加到容器中for (auto val : list) {data.push_back(val);}}void print() {for (auto val : data) {std::cout << val << " ";}std::cout << std::endl;}
};int main() {MyContainer c{1, 2, 3, 4}; // 用{}初始化,自动匹配initializer_list构造函数c.print(); // 输出:1 2 3 4 return 0;
}
(3) STL容器中的应用

所有 STL 容器(如 vectormapset 等)在 C++11 中都新增了接收 std::initializer_list 的构造函数,因此可以直接用 {} 初始化:

#include <vector>
#include <map>int main() {// vector用initializer_list初始化std::vector<int> vec{1, 2, 3};// map用initializer_list初始化(元素是键值对)std::map<std::string, int> score{{"Alice", 90}, {"Bob", 85}};return 0;
}
(4)作为返回值

函数可以返回 std::initializer_list<T> 类型,允许用 {} 直接返回元素列表。

#include <initializer_list>
#include <iostream>std::initializer_list<int> get_numbers() {return {1, 2, 3, 4}; // 返回initializer_list
}int main() {for (auto num : get_numbers()) {std::cout << num << " "; // 输出:1 2 3 4 }return 0;
}
5.4关键注意事项
  1. 元素的生命周期
    std::initializer_list 中的元素存储在栈上的临时内存中,其生命周期与 std::initializer_list 对象一致。不能保存指向这些元素的指针或引用,否则会导致悬垂指针:

    #include <initializer_list>int main() {std::initializer_list<int> list = {1, 2, 3};const int* p = list.begin(); // 指向临时元素list = {4, 5, 6}; // 原临时元素已销毁,p变为悬垂指针return 0;
    }
    
  2. 元素是常量
    std::initializer_list<T> 中的元素是 const T 类型,不允许修改:

    #include <initializer_list>int main() {std::initializer_list<int> list = {1, 2, 3};// list[0] = 10; // 错误:元素是常量,不允许修改return 0;
    }
    
  3. 与其他构造函数的优先级
    当类同时有接收 std::initializer_list 的构造函数和其他构造函数时,列表初始化({})会优先匹配 initializer_list 构造函数

    #include <initializer_list>
    #include <iostream>class MyClass {
    public:MyClass(int a, int b) {std::cout << "调用(int, int)构造函数" << std::endl;}MyClass(std::initializer_list<int> list) {std::cout << "调用initializer_list构造函数" << std::endl;}
    };int main() {MyClass obj1(1, 2); // 调用(int, int)构造函数MyClass obj2{1, 2}; // 优先调用initializer_list构造函数return 0;
    }
http://www.dtcms.com/a/338404.html

相关文章:

  • GitHub Actions 从核心思想到最佳实践
  • 宋红康 JVM 笔记 Day04|双亲委派机制、沙箱安全机制、类的自动使用与被动使用
  • 电子电气架构 --- 软件会给汽车带来哪些变化?
  • 鸿蒙生态7月技术月报 | HarmonyOS 5.1 开发特性详解
  • 蓝池参与雅江水电工程融资,助力国家基础设施建设与经济发展
  • 08.常见文本处理工具
  • 03.文件管理和操作命令
  • 解读60页全面认识大数据基础知识培训【附全文阅读】
  • 8.18 打卡 DAY 45 Tensorboard使用介绍
  • Mysql——前模糊索引失效原因及解决方式
  • 深度强化学习之前:强化学习如何记录策略与价值?
  • Java面试题储备14: 使用aop实现全局日志打印
  • Nodejs学习
  • 【SkyWalking】单节点安装
  • Linux命令大全-rmdir命令
  • Java中的 “128陷阱“
  • vue从入门到精通:轻松搭建第一个vue项目
  • go语言条件语if …else语句
  • rem 响应式布局( rem 详解)
  • 鼠标右键没有“通过VSCode打开文件夹”
  • FreeRTOS【3-1】创建第一个多任务程序复习笔记
  • STM32驱动SG90舵机全解析:从PWM原理到多舵机协同控制
  • Sring框架-IOC篇
  • ​​Java核心知识体系与集合扩容机制深度解析​
  • JavaSE高级-02
  • JDBC的使用
  • 【Python】Python Socket 网络编程详解:从基础到实践​
  • Street Crafter 阅读笔记
  • IDEA创建项目
  • MYSQL中读提交的理解