突破编程_C++_C++11新特性(列表初始化)

1 基本概念与使用

C++11 引入了许多新特性,其中列表初始化(List Initialization)或统一初始化(Uniform Initialization)是其中之一。列表初始化是一种新的语法,用于初始化对象,可以使得代码更加清晰、直观,并且有助于防止某些错误。

(1)列表初始化的基本语法

列表初始化使用花括号 {} 来包围初始化列表。例如:

int a{10};             // 基本类型  
std::vector<int> v{1, 2, 3, 4, 5}; // STL 容器  
std::pair<int, int> p{1, 2};        // STL 对

(2)列表初始化与函数参数

列表初始化也可以用于函数调用,这有助于消除一些歧义,特别是在涉及到多个重载函数时:

void foo(int a);  
void foo(const std::initializer_list<int>& list);  foo(10);         // 调用 foo(int a)  
foo({10});       // 调用 foo(const std::initializer_list<int>& list)

(3)列表初始化与构造函数

列表初始化会影响构造函数的选择。如果类有多个构造函数,并且其中一个接受 std::initializer_list 参数,那么使用列表初始化时,会调用这个构造函数:

class MyClass 
{  
public:  MyClass(int a);                  // 构造函数1  MyClass(const std::initializer_list<int>& list); // 构造函数2  
};  MyClass obj1(10);    // 调用构造函数1  
MyClass obj2{10};    // 调用构造函数2,因为使用了列表初始化

(4)列表初始化与类成员

在类的构造函数体内,列表初始化也可以用于初始化成员变量:

class MyClass 
{  
public:  MyClass(int val, const std::string& str) : x{val}, s{str} {}
private:  int x;  std::string s;    
};

(5)列表初始化创建结构体对象与类对象

列表初始化可以用来创建结构体(struct)对象和类(class)对象。结构体和类在 C++ 中都是用户定义的类型(UDT),它们的主要区别在于访问控制(如公有、私有和保护成员)和继承方面的不同,但在初始化方面,它们通常使用相同的方法。

如果结构体与类的所有成员变量都是公有的,则可以直接使用列表初始化:

#include <iostream>  
#include <string>  // 定义一个结构体  
struct PersonStruct {std::string name;int age;
};// 定义一个类  
class PersonClass {
public:std::string name;int age;
};int main() 
{// 使用列表初始化创建结构体对象  PersonStruct personStruct1 = { "Alice", 30 };// 使用列表初始化创建类对象PersonClass personClass1 = { "Bob", 25 };   return 0;
}

如果成员变量是私有或者保护的,则需要使用构造函数:

#include <iostream>  
#include <string>  class PersonClass {
public:// 如果没有该构造函数,则列表初始化会失败PersonClass(const std::string& name, int age) : name(name), age(age) {}
private:std::string name;int age;
};int main() 
{PersonClass personClass1 = { "Bob", 25 };   return 0;
}

(6)列表初始化与数组

列表初始化也可以用于初始化数组:

int arr1[3] = {1, 2, 3};   // C++98风格  
int arr2[] = {1, 2, 3};    // C++11风格,数组大小由初始化器确定

(7)自动类型推导

在使用auto关键字声明变量时,列表初始化可以自动推导变量的类型:

auto x = {10}; // x的类型是std::initializer_list<int>  
auto y{10};    // y的类型是int,因为直接列表初始化不会推导为initializer_list

注意,当使用 = 进行初始化时,如果初始化器是列表,则 auto 会推导为 std::initializer_list 类型。而直接使用 {} 进行初始化时,则会根据列表的内容推导为具体的类型。

(8)嵌套列表初始化

对于包含嵌套结构的对象,列表初始化同样适用:

std::pair<int, std::vector<int>> p{1, {2, 3, 4}};

在这个例子中,p 是一个 pair,其第一个元素是 int 类型,第二个元素是一个 vector<int> 类型。列表初始化允许同时初始化这两个元素。

总体而言,C++11的列表初始化(统一初始化)提供了一种更加清晰、直观的方式来初始化对象。它有助于消除一些歧义,特别是在涉及到重载函数和多个构造函数时。

2 防止类型收窄

列表初始化不仅提供了一种更直观、更统一的对象初始化方式,而且它还能有效地防止类型收窄(narrowing)。

2.1 类型收窄的概念与防止

类型收窄是指在赋值过程中,源类型的值无法精确表示为目标类型的值,从而导致数据丢失或精度降低。

在 C++11 之前,使用圆括号或等号进行初始化时,编译器往往不会检查类型收窄的问题,这可能导致一些难以察觉的错误。然而,在使用列表初始化时,编译器会进行更严格的检查,并在发现类型收窄时发出警告或错误。

类型收窄通常发生在以下几种情况:

  • 浮点数到整数的转换:当浮点数的值无法精确表示为整数时。
  • 大整数到小整数的转换:当源整数的值超出目标整数类型的表示范围时。
  • 字符到整数的转换:当字符的 ASCII 值无法精确表示为整数时(虽然这种情况较少见)。

列表初始化通过以下方式防止类型收窄:

  • 在初始化时,如果源类型的值无法精确表示为目标类型的值,编译器会发出警告或错误。
  • 适用于基本类型、结构体、联合体以及类的初始化。

如下为一些样例代码:

(1)浮点数到整数的转换

double d = 3.14;  
int a(d);         // 可能的类型收窄,但 C++11 之前的编译器通常不会警告  
int b{d};         // 类型收窄,C++11 编译器会发出警告或错误

在上面的例子中,将 double 类型的值 3.14 转换为 int 类型时,小数部分会被丢弃。使用圆括号初始化时,编译器可能不会发出警告。但是,使用列表初始化时,编译器会检测到这种类型收窄,并发出警告或错误。

(2)大整数到小整数的转换

long long bigNum = 1234567890123456789LL;  
int smallNum(bigNum);       // 可能的类型收窄,但 C++11 之前的编译器通常不会警告  
int smallNumList{bigNum};   // 类型收窄,C++11 编译器会发出警告或错误

在这个例子中,尝试将一个非常大的 long long 整数赋值给一个 int 变量。由于 int 类型的范围远小于 long long,这种转换会导致数据丢失。使用列表初始化时,编译器会检测到这种类型收窄。

2.2 如何避免类型收窄

要避免类型收窄,可以采取以下策略:

(1)显式转换: 如果确实需要进行类型转换,并且你确定转换是安全的,可以使用显式类型转换(如静态转换 static_cast)。但请注意,即使使用显式转换,如果转换会导致数据丢失,编译器仍然可能发出警告。

double d = 3.14;  
int a = static_cast<int>(d); // 显式转换,但仍然可能导致数据丢失

(2)使用合适的类型: 在设计程序时,尽量使用能够精确表示所需数据的类型。如果可能的话,避免使用会导致类型收窄的转换。
(3)利用编译器警告: 确保编译器设置为在发现可能的类型收窄时发出警告。这可以帮助在编写代码时及时发现问题。

3 限制和注意事项

(1)类型不匹配: 列表初始化不会自动进行类型转换。如果初始化器的类型与目标类型不匹配,且没有合适的构造函数可以接受该初始化器,则会导致编译错误。

#include <iostream>  class MyClass {
public:MyClass(int value) {std::cout << "Constructed with int: " << value << std::endl;}// 注意:没有定义接受double类型的构造函数  // MyClass(double value); // 假设这个构造函数不存在  
};int main() 
{// 使用圆括号进行初始化时,会尝试类型转换(如果有合适的构造函数)  MyClass obj1(3.14); // 编译器会输出告警 // 使用列表初始化时,不会进行类型转换  MyClass obj2{ 3.14 }; // 错误:没有合适的构造函数接受 double 类型的参数,编译失败return 0;
}

上面的代码定义了一个 MyClass 类,它有一个接受 int 类型参数的构造函数。在 main 函数中,试图用一个 double 类型的值(3.14)来初始化 MyClass 的对象。

当使用圆括号进行初始化时(MyClass obj1(3.14);),编译器会尝试找到一个合适的构造函数来接受这个 double 类型的值。如果存在一个接受 double 的构造函数,或者存在一个接受 int 的构造函数并且编译器能够自动将 double 转换为 int,那么初始化就会成功。

当使用列表初始化时(MyClass obj2{3.14};),编译器不会尝试进行任何类型转换。它会直接查找一个接受 double 类型参数的构造函数,但因为没有定义这样的构造函数,所以编译器会报错,指出没有合适的构造函数来接受这个初始化器。

(2)静态成员初始化: 列表初始化不能用于直接在类定义中初始化静态成员变量。

#include <iostream>  class MyClass {
public:MyClass()  {}public:static int myInt{12}; // 错误:不能在类定义中直接初始化静态成员,编译失败
};

静态成员变量的初始化不能在类定义内部进行,而必须在类定义外部完成:

#include <iostream>  class MyClass {
public:MyClass()  {}public:static int myInt;  
};int MyClass::myInt{ 12 };

(3)位字段: 列表初始化不能用于直接初始化位字段。

class MyClass {
public:MyClass() {}private:unsigned int myBitField: 4; // 位字段  // 下面的尝试是错误的,不能使用列表初始化直接初始化位字段  // unsigned int myBitField : 4 {2}; // 错误:不能使用列表初始化直接初始化位字段  
};

对于位字段,C++ 标准没有提供直接在类定义中进行初始化的机制,因为位字段的初始化通常依赖于具体的实现细节和存储布局。位字段的初始化通常是在构造函数的初始化列表中完成的,但即使在那里,也只能通过赋值来进行,而不是通过列表初始化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/756564.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CSS学习2

自己在工作中总是有一些自动化开发的需求&#xff0c;总是以为自己是有前端基础的&#xff0c;但是一写页面&#xff0c;布局都布不好&#xff0c;真是搞笑&#xff0c;说起来还是基本功不扎实啊&#xff0c;这里在重新复习一下&#xff0c;然后记录一下文档。后边在写两个综合…

C++从零开始(day54)——位图,布隆过滤器

这是关于一个普通双非本科大一学生的C的学习记录贴 在此前&#xff0c;我学了一点点C语言还有简单的数据结构&#xff0c;如果有小伙伴想和我一起学习的&#xff0c;可以私信我交流分享学习资料 那么开启正题 今天分享的是关于位图&#xff0c; 布隆过滤器相关的知识点 1.位…

【linux】环境变量(进程二)

这里写目录标题 命令行参数&#xff1a;环境变量&#xff1a; 命令行参数&#xff1a; 不谈命令行参数就谈环境变量就是耍流氓。 相信我们在C语言阶段都在main函数里见过参数。 例如int main(int argc, char* argv[]) 这是什么东西呢&#xff1f; 话不多说我们直接打印一下看…

java.lang.ArithmeticException: Rounding necessary错误解决方法

今天使用BigDecimal的setScale方法控制小数位时遇到了java.lang.ArithmeticException: Rounding necessary错误&#xff0c;经查&#xff0c;setScale方法有2中使用方法&#xff1a; 1.只设置小数位 setScale(n)&#xff0c;例如&#xff1a; BigDecimal bigDecimal new Bi…

JupyterNotebook 如何切换使用的虚拟环境kernel

在Jupyter Notebook中&#xff0c;如果需要修改使用的虚拟环境Kernel&#xff1a; 首先&#xff0c;需要确保虚拟环境已经安装conda上【conda基本操作】 打开Jupyter Notebook。 在Jupyter Notebook的顶部菜单中&#xff0c;选择 “New” 在弹出的窗口中&#xff0c;列出了…

“JavaScript: void(0)的替代方案有哪些?”

学习目标&#xff1a; 理解javascript:void(0)的工作原理&#xff0c;以及它在前端开发中的作用和用途。掌握javascript:void(0)的正确用法&#xff0c;包括在HTML中使用和在事件处理程序中使用。能够识别javascript:void(0)可能引起的常见问题&#xff0c;并学会相应的解决方…

python 基础语法

python 基础语法 一、基础语法1. 标识符2. python保留字3. 注释分类4. 行与缩进5. 数字(Number)类型6. 字符串(String)7. 空行分割8. 等待用户输入9. 多个语句构成代码组10. import 与 from...import 二、代码概览 一、基础语法 1. 标识符 第一个字符必须是字母表中字母或下划…

numpy的数组深度叠加dstack详解

☞ ░ 前往老猿Python博客 ░ https://blog.csdn.net/LaoYuanPython 一、引言 numpy的dstack函数用于沿着第三个维度&#xff08;深度&#xff09;将数组堆叠起来。它将多个数组按照深度方向进行堆叠&#xff0c;生成一个新的数组。具体来说&#xff0c;dstack函数会将输入的…

docker部署开源多功能监控系统

HertzBeat 是一个无需 Agent、高性能、易扩展、功能强大的开源实时监控告警系统&#xff0c;无需 Agent、高性能、易扩展、功能强大&#xff0c;由 Dromara 团队开发并开源&#xff0c;能够帮我们轻松监控应用、服务、基础设施等各种资源的运行状况 部署 docker run -d -p 11…

腾讯云服务器按月收费价格表,优惠价格5元一个月起

2024腾讯云服务器多少钱一个月&#xff1f;5元1个月起&#xff0c;腾讯云轻量服务器4核16G12M带宽32元1个月、96元3个月&#xff0c;8核32G22M配置115元一个月、345元3个月&#xff0c;腾讯云轻量应用服务器61元一年折合5元一个月、4核8G12M配置646元15个月、2核4G5M服务器165元…

2024 年 AI 辅助研发趋势-人才培养与教育

2024 年 AI 辅助研发趋势随着人工智能技术的持续发展与突破&#xff0c;2024年AI辅助研发正成为科技界和工业界瞩目的焦点。从医药研发到汽车设计&#xff0c;从软件开发到材料科学&#xff0c;AI正逐渐渗透到研发的各个环节&#xff0c;变革着传统的研发模式。在这一背景下&am…

01 JDBC介绍

文章目录 JDBC本质版本使用核心APIDriverDriverManager驱动注册连接对象获取 Connection获取执行对象事务管理 Statement概述 ResultSet概述 JDBC本质 官方&#xff08;sun公司&#xff09;定义的一套操作所有关系型数据库的规则&#xff0c;即接口各个数据库厂商去实现这套接…

im-system学习

文章目录 LimServerLimServersnakeyaml依赖使用配置类配置文件 私有协议解码MessageDecoderByteBufToMessageUtils 这个很全&#xff1a; IM即时通讯系统[SpringBootNetty]——梳理&#xff08;总&#xff09; IO线程模型 Redis 分布式客户端 Redisson 分布式锁快速入门 Lim…

【Week Y2】使用自己的数据集训练YOLO-v5s

Y2-使用自己的数据集训练YOLO-v5s 零、遇到的问题汇总&#xff08;1&#xff09;遇到git的import error&#xff08;2&#xff09;Error&#xff1a;Dataset not found&#xff08;3&#xff09;Error&#xff1a;删除中文后&#xff0c;训练图片路径不存在 一、.xml文件里保存…

框架篇常见面试题

1、Spring框架的单例bean是线程安全的吗&#xff1f; 2、什么是AOP&#xff1f; 3、Spring的事务是如何实现的&#xff1f; 4、Spring事务失效的场景 5、SpringBean的声明周期 6、Spring的循环依赖 7、SpringMVC的执行流程 8、SpringBoot自动配置原理 9、Spring常见注解

Java中的实用类讲解(中篇)

如果想观看更多Java内容 可上我的个人主页关注我&#xff0c;地址子逸爱编程-CSDN博客https://blog.csdn.net/a15766649633?spm1000.2115.3001.5343 使用工具 IntelliJ IDEA Community Edition 2023.1.4 使用语言 Java8 代码能力快速提升小方法&#xff0c;看完代码自己敲…

AV1:帧内预测(一)

​VP9支持10种帧内预测模式&#xff0c;包括8种角度模式和非角度模式DC、TM(True Motion)模式&#xff0c;AV1在其基础上进一步扩展&#xff0c;AV1帧内预测角度模式更细化&#xff0c;同时新增了部分非角度模式。 扩展的角度模式 AV1在VP9角度模式的基础上进一步扩展&#xf…

CSharp的lambda表达式匿名类扩展方法

c#的lamba表达式 之前已经写过一些关于委托还有事件的文章&#xff0c;今天就来介绍一下lambda表达式。 首先定义需要的函数以及委托 { public delegate void DoNothingDelegate(); public delegate void StudyDelegate(int id, string name);private void DoNothing() {Cons…

P1678 烦恼的高考志愿(二分查找)

题目描述 现有 m 所学校&#xff0c;每所学校预计分数线是 ai​。有 n 位学生&#xff0c;估分分别为 bi​。 根据 n 位学生的估分情况&#xff0c;分别给每位学生推荐一所学校&#xff0c;要求学校的预计分数线和学生的估分相差最小&#xff08;可高可低&#xff0c;毕竟是估分…

蓝桥杯day3刷题日记--P9420 [蓝桥杯 2023 国 B] 子 2023 / 双子数

2023 思路&#xff1a;首先先用to_string把数字变成字符串&#xff08;ps&#xff1a;在Dev c里用不了&#xff09;&#xff0c;用数组dp&#xff0c;用dp【0】记录2的数量&#xff0c;dp【1】记录20的数量&#xff0c;dp【2】记录202的数量&#xff0c;dp【3】记录2023的数量…