【C++11】:统一的列表初始化|声明|STL变化

                                               🎬慕斯主页修仙—别有洞天

                                              ♈️今日夜电波:マイノリティ脈絡—ずっと真夜中でいいのに。

                                                                0:24━━━━━━️💟──────── 4:02
                                                                    🔄   ◀️   ⏸   ▶️    ☰  

                                      💗关注👍点赞🙌收藏您的每一次鼓励都是对我莫大的支持😍


目录

统一的列表初始化

使用初始化列表时,可添加等号(=),也可不添加

创建对象时也可以使用列表初始化方式调用构造函数初始化

std::initializer_list

基本概念

使用场景

底层原理

注意事项

声明

auto

decltype

nullptr

STL中的变化

array

1. 包含头文件

2. 定义array

3. 初始化array

4. 访问array元素

5. array成员函数

6. array与内置数组的区别

forward_list

1. 包含头文件

2. 定义和初始化 forward_list

3. 访问 forward_list

4. 插入和删除元素

5. 其他操作

注意事项


统一的列表初始化

        在C++98中,标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定,而C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用初始化列表时,可添加等号(=),也可不添加

使用初始化列表时,可添加等号(=),也可不添加

  struct test
  {
  	int _x;
  	char _str;
  };
  
  int array1[] = { 1, 2, 3, 4, 5 };
	int array2[5] = { 0 };
	test aa = { 2,'r' };
	int x1 = 1;
  
	//C++11后以下也支持:
	int x2{ 2 };
	int array3[]{ 1, 2, 3, 4, 5 };
	int array4[5]{ 0 };
	test bb{ 2,'s' };

创建对象时也可以使用列表初始化方式调用构造函数初始化

class Date
{
public:
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)
	{
		cout << "Date(int year, int month, int day)" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2022, 1, 1); // old style
  
	// C++11后支持的列表初始化,这里会调用构造函数初始化
	Date d2{ 2022, 1, 2 };
	Date d3 = { 2022, 1, 3 };
	return 0;

  const Date& d4 = { 2023, 11, 25 };
  
  Date* p1 = new Date[3]{ d1, d2, d3 };
 	Date* p2 = new Date[3]{ {2022, 11, 25}, {2022, 11, 26}, {2022, 11, 27} };
}

        需要注意的是:上述使用列表初始化方式Date d3 = { 2022, 1, 3 }的本质也是类型转换 构造+拷贝构造-》优化直接构造

std::initializer_list

    std::initializer_list 是C++11标准引入的一个模板类,用于表示对象的初始化列表。它提供了一种方便的方式来初始化对象,尤其是在构造函数和函数中处理可变数量的参数时。下面是对 std::initializer_list 的详细解释:

基本概念

    std::initializer_list 是一个轻量级的只读容器,它内部维护了一个指向堆上的数组以及该数组的长度。它不支持添加、删除元素等操作,但可以使用迭代器访问元素,以及使用 size() 函数获得容器长度

        当使用花括号 {} 进行列表初始化时,编译器会尝试构造一个 std::initializer_list 对象。例如:

std::initializer_list<int> ilist = {1, 2, 3, 4, 5};

        在这个例子中,ilist 是一个 std::initializer_list<int> 类型的对象,它包含5个整数。

 

使用场景

        构造函数和赋值运算符:如果类的构造函数或赋值运算符接受一个 std::initializer_list 参数,那么可以使用花括号初始化列表来初始化或赋值对象。

class MyClass {
public:
    MyClass(std::initializer_list<int> list) {
        // 处理 list 中的元素
    }
};

MyClass obj({1, 2, 3}); // 使用 std::initializer_list 构造函数

         函数参数:函数也可以接受 std::initializer_list 参数,从而能够接收任意数量的元素。

void printNumbers(std::initializer_list<int> numbers) {
    for (int num : numbers) {
        std::cout << num << ' ';
    }
    std::cout << std::endl;
}

printNumbers({1, 2, 3, 4, 5}); // 输出: 1 2 3 4 5

        范围循环std::initializer_list 可以与范围循环(range-based for loop)一起使用,以迭代其元素。 

std::initializer_list<int> ilist = {1, 2, 3, 4, 5};
for (int i : ilist) {
    std::cout << i << ' '; // 输出: 1 2 3 4 5
}

        使用{ }初始化容器 

        可以发现C++11后所有的容器都适配了std::initializer_list(这里只举几个例子)。这也使得所有的容器都可使用花括号进行初始化:

        我们像如下对容器使用{ }:

vector<int> v = { 1,2,3,4 };
list<int> lt = { 1,2 };
// 这里{"sort", "排序"}会先初始化构造一个pair对象
map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };
// 使用大括号对容器赋值
v = {10, 20, 30};

 

底层原理

        从编译器底层中使用std::initializer_list用{ }来初始化的话,他会找一个常量区将{ }内的数组内的内容储存起来,然后在std::initializer_list这个类下会有两个指针分别指向这段空间的开始和结束的下一个位置,他支持迭代器,而这个迭代器是原生指针。因此我们可以利用该特性合理的使用std::initializer_list,让我们的类也支持用{ }来初始化。如下例子让模拟实现的vector也支持{}初始化和赋值

template<class T>
class vector {
public:
	typedef T* iterator;
	vector(initializer_list<T> l)
	{
		_start = new T[l.size()];
		_finish = _start + l.size();
		_endofstorage = _start + l.size();
		iterator vit = _start;
		typename initializer_list<T>::iterator lit = l.begin();
		while (lit != l.end())
		{
			*vit++ = *lit++;
		}
		//for (auto e : l)
		// *vit++ = e;
	}
	vector<T>& operator=(initializer_list<T> l) {
		vector<T> tmp(l);
		std::swap(_start, tmp._start);
		std::swap(_finish, tmp._finish);
		std::swap(_endofstorage, tmp._endofstorage);
		return *this;
	}
private:
	iterator _start;
	iterator _finish;
	iterator _endofstorage;
};

 

注意事项
  • std::initializer_list 中的元素是常量,不能修改。
  • 不要将 std::initializer_list 用于期望长期存储值的场景,因为它通常只包含对临时数组的引用。
  • 在类的构造函数中,如果参数为 std::initializer_list 类型,则需要在参数类型前加上花括号 {}

声明

auto

        在C++98中auto是一个存储类型的说明符,表明变量是局部自动存储类型,但是局部域中定义局部的变量默认就是自动存储类型,所以auto就没什么价值了。C++11中废弃auto原来的用法,将其用于实现自动类型腿断。这样要求必须进行显示初始化,让编译器将定义对象的类型设置为初始化值的类型。

 

decltype

        关键字decltype将变量的类型声明为表达式指定的类型。 decltype可以推导对象的类型。这个类型是可以用的,他可以用来作为模板实参,或者再定义对象如下:

	int i = 1;
	double d = 2.2;
	auto j = i;

	auto ret = i * d;
	decltype(ret) x;

	vector<decltype(ret)> v;
	v.push_back(1);
	v.push_back(1.1);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

nullptr

        由于C++中NULL被定义成字面量0,这样就可能回带来一些问题,因为0既能指针常量,又能表示整形常量。所以出于清晰和安全的角度考虑,C++11中新增了nullptr,用于表示空指针。

 

STL中的变化

        对于之前的容器做出了新增cbegin()、cend()、带initializer_list的构造赋值等等较为轻微的变化 ,较为大的变化为如下为新增的容器:

        对于unordered_map和unordered_set我们还是比较熟悉的了,相比map和set而言:

  • map 和 set 是基于红黑树实现的,这意味着它们的元素在内部是有序的。
  • unordered_map 和 unordered_set 是基于哈希表实现的,因此它们的元素在内部是无序的。

        对此不多叙述。下面详细介绍一下array和forward_list这两个容器:

array

        在C++中,array是一个固定大小的序列容器,它包含相同类型的元素,并且其大小在编译时确定。array是C++标准库<array>头文件中的一个模板类,它提供了对数组元素的随机访问迭代器,并支持一系列成员函数来操作这些元素。

 

1. 包含头文件

        使用array之前,你需要包含<array>头文件:

#include <array>

2. 定义array

        你可以使用以下语法定义一个array

std::array<类型, 大小> 变量名;

        其中,类型array中元素的类型,大小array中元素的数量,它是一个常量表达式,必须在编译时确定。

        例如,定义一个包含5个整数的array

std::array<int, 5> myArray;

3. 初始化array

        可以使用列表初始化来初始化array

std::array<int, 5> myArray = {1, 2, 3, 4, 5};

        也可以使用fill构造函数来初始化所有元素为同一值:

std::array<int, 5> myArray(10); // 所有元素初始化为10

4. 访问array元素

        你可以使用下标操作符[]来访问array中的元素:

int firstElement = myArray[0]; // 访问第一个元素  
myArray[2] = 15; // 修改第三个元素

5. array成员函数

        如下可见分别为,是否常量正向,是否常量反向使用的迭代器:    

        如下为返回大小、最大的大小、判空、使用[]、at来修改数组内的值、返回最前面的值和最后面的值。我们可能没见过data这个函数,这里的作用为:返回指向数组对象中第一个元素的指针。下面fill则是的将 val 设置为数组对象中所有元素的值。swap用的较多,即为交换两个数组。

    array提供了许多有用的成员函数,例如size()来获取数组大小,begin()end()来获取指向数组第一个和最后一个元素的迭代器,以及at()来访问元素(如果索引越界,会抛出std::out_of_range异常)。

	std::array<int, 6> myarray;
	std::array<int, 6> myarray2 = { 0 };//myarray2{};全部初始化为0  myarray2{1,2,3};部分初始化,其他为0
	myarray.fill(5);
	myarray.swap(myarray2);
	cout << myarray[2] << endl;
	myarray.at(2) = 3;
	cout << myarray.size() << endl;
	cout << myarray.max_size() << endl;
	cout << myarray.empty()<< endl;
	cout << myarray2.front() << endl;
	cout << myarray2.back() << endl;
	cout << myarray2.data() << endl;

6. array与内置数组的区别

        与内置数组相比,std::array有以下优点:

  • 固定大小std::array的大小在编译时确定,这有助于捕获潜在的错误,并可能优化性能。
  • 支持STL算法std::array可以与STL算法无缝集成,而内置数组则不可以。
  • 安全性std::arrayat()成员函数在索引越界时会抛出异常,而内置数组则可能导致未定义行为。
  • 更好的接口std::array提供了更丰富的接口和成员函数,使得操作更加便捷。

forward_list

    forward_list 是 C++ 标准库中的一个容器,它表示一个单向链表。与 list 容器不同,forward_list 只允许从前往后遍历元素,不支持从后往前遍历。因此,forward_list 通常比 list 更节省空间,但在某些操作上可能稍慢一些。

        以下是 forward_list 的一些基本特性和用法:

1. 包含头文件

        使用 forward_list 需要包含 <forward_list> 头文件:

#include <forward_list>

2. 定义和初始化 forward_list

        你可以使用花括号初始化列表或 insertemplace 等成员函数来创建和填充 forward_list

	std::forward_list<int> myList = { 1, 2, 3, 4, 5 };
	std::forward_list<int> mylist2;
	std::forward_list<int> mylist3(10);//创建包涵10个元素的容器
	std::forward_list<int> mylist4(10,5);//创建包涵10个元素的容器并且全初始化为5
	std::array<int, 6> myarray2 = { 1,2,3,4,5 };
	std::forward_list<int> mylist5(myarray2.begin(), myarray2.end());//使用其他容器的迭代器初始化

3. 访问 forward_list

        由于 forward_list 是单向的,你只能使用迭代器从前往后遍历它。

for (auto it = myList.begin(); it != myList.end(); ++it) {
    std::cout << *it << " ";
}

        或者,使用基于范围的 for循环:

for (const auto& elem : myList) {
    std::cout << elem << " ";
}

4. 插入和删除元素

        使用assign()用新元素替换容器中原有内容。emplace_front()和push_front()在容器头部插入一个元素。但emplace_front()效率更高。insert_after()和emplace_after(),在指定位置之后插入一个新元素,并返回一个指向新元素的迭代器。但emplace_after()效率更高。pop_front(),删除容器头部的一个元素。erase_after(),删除容器中某个指定位置之后或区域内的所有元素。

  std::forward_list<int> first;
  std::forward_list<int> second;

  first.assign (4,15);                           // 15 15 15 15

  second.assign (first.begin(),first.end());     // 15 15 15 15

  first.assign ( {77, 2, 16} );                  // 77 2 16
///
  std::forward_list< std::pair<int,char> > mylist;

  mylist.emplace_front(10,'a');//push_front()
  mylist.emplace_front(20,'b');
  mylist.emplace_front(30,'c');

///
  std::forward_list< std::pair<int,char> > mylist;
  auto it = mylist.before_begin();

  it = mylist.emplace_after ( it, 100, 'x' );
  it = mylist.emplace_after ( it, 200, 'y' );
  it = mylist.emplace_after ( it, 300, 'z' );

///
  std::forward_list<int> mylist = {10, 20, 30, 40, 50};
                                            // 10 20 30 40 50
  auto it = mylist.begin();                 // ^

  it = mylist.erase_after(it);              // 10 30 40 50
                                            //    ^
  it = mylist.erase_after(it,mylist.end()); // 10 30

        下面主要介绍其中比较特殊的两个迭代器before_begin和cbefore_begin。这两个迭代器指向容器中第一个元素之前的位置。只是后面的是常量迭代器而已

  std::forward_list<int> mylist = {20, 30, 40, 50};

  mylist.insert_after ( mylist.before_begin(), 11 );//11 20 30 40 50
  mylist.insert_after ( mylist.cbefore_begin(), 19 ); //19 11 20 30 40 50
5. 其他操作

    forward_list 还提供了许多其他成员函数,empty(检查列表是否为空)、size(返回列表中元素的数量)、swap()(交换两个容器中的元素)、resize()(调整容器的大小)、clear()(删除容器存储的所有元素),sort()(排序)。reverse()(翻转顺序)。主要介绍如下:splice_after(),将某个 forward_list 容器中指定位置或区域内的元素插入到另一个容器的指定位置之后。remove(val),删除容器中所有等于 val 的元素。remove_if(),删除容器中满足条件的元素。unique(),删除容器中相邻的重复元素,只保留一个。merge(),合并两个事先已排好序的 forward_list 容器,并且合并之后的 forward_list 容器依然是有序的。

  std::forward_list<int> first = { 1, 2, 3 };
  std::forward_list<int> second = { 10, 20, 30 };

  auto it = first.begin();  // points to the 1

  first.splice_after ( first.before_begin(), second );
                          // first: 10 20 30 1 2 3
                          // second: (empty)
                          // "it" still points to the 1 (now first's 4th element)
  second.splice_after ( second.before_begin(), first, first.begin(), it);
                          // first: 10 1 2 3
                          // second: 20 30

  first.splice_after ( first.before_begin(), second, second.begin() );
                          // first: 30 10 1 2 3
                          // second: 20
                          // * notice that what is moved is AFTER the iterator

///
bool single_digit (const int& value) { return (value<10); }

// a predicate implemented as a class:
class is_odd_class
{
public:
  bool operator() (const int& value) {return (value%2)==1; }
} is_odd_object;

  std::forward_list<int> mylist = {7, 80, 7, 15, 85, 52, 6};

  mylist.remove_if (single_digit);      // 80 15 85 52

  mylist.remove_if (is_odd_object);     // 80 52

///
  std::forward_list<double> first = {4.2, 2.9, 3.1};
  std::forward_list<double> second = {1.4, 7.7, 3.1};
  first.sort();
  second.sort();
  first.merge(second); //first: 1.4 2.9 3.1 3.1 4.2 7.7

注意事项
  • 由于 forward_list 是单向的,它不支持 reverse_iterator,因此不能使用 rbegin()rend() 函数。
  • 在某些情况下,forward_list 可能比 listvector 等其他容器更适合,特别是在你需要一个简单的单向链表并且空间效率很重要的时候。但是,由于它只支持单向遍历,因此在某些算法上可能不如双向链表或数组高效。

 


                         感谢你耐心的看到这里ღ( ´・ᴗ・` )比心,如有哪里有错误请踢一脚作者o(╥﹏╥)o! 

                                       

                                                                        给个三连再走嘛~  

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

已知屏幕分辨率和屏幕尺寸,JavaScript如何计算屏幕PPI像素密度

返回主目录:OpenLayers扩展组件系列汇总目录:常用OpenLayers地图扩展组件ol-ext、ol-cesium、ol-layerswitcher、ol-geocoder和ol-wind等扩展库 前言 本章作为补充章,用于讲解使用ol-ext组件的前置知识。 要想知道 PPI 是什么,我们需要先理解“像素”这个概念,那么什么是…

【C++算法】洛谷P1102:A-B数对,思路,lower_bound,upper_bound,二分答案,代码详解

文章目录 1&#xff09;解题思路2&#xff09;三种情况3&#xff09;代码 题目链接&#xff1a;P1102 A-B 数对 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 1&#xff09;解题思路 这道题要求我们在序列中找到 A − B C A-BC A−BC 的数对的个数&#xff0c;下标不同的数…

注解总结,Java中的注解,springboot中的注解

注解总结 1、Junit 开始执行的方法&#xff1a;初始化资源&#xff0c;执行完之后的方法&#xff1a;释放资源 测试方法&#xff0c;必须是&#xff1a;公有、非静态、无参无返回值的 在一个类中&#xff0c;可以定义多个测试方法&#xff0c;每个测试方法可以单独运行&#…

XUbuntu22.04之安装Plantuml(二百二十三)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

llvm后端

SelectionDAGBuilder是LLVM&#xff08;Low Level Virtual Machine&#xff09;编译器中的一个重要组件&#xff0c;它负责将LLVM中间表示&#xff08;Intermediate Representation&#xff0c;IR&#xff09;转换为SelectionDAG&#xff08;选择有向无环图&#xff09;的形式。…

Nacos部署(四)Docker部署Nacos2.3.x集群环境

&#x1f60a; 作者&#xff1a; 一恍过去 &#x1f496; 主页&#xff1a; https://blog.csdn.net/zhuocailing3390 &#x1f38a; 社区&#xff1a; Java技术栈交流 &#x1f389; 主题&#xff1a; Nacos部署&#xff08;四&#xff09;Docker部署Nacos2.3.x集群环境 ⏱…

adams卸载与安装

adams 卸载后重新安装lnstaller could not read the log directory of the existing installerto backup and restore.Installer will now exit. ADAMS软件卸载安装【adams吧】_百度贴吧 (baidu.com)

阿里云OSS存储的视频如何加水印

OSS是不能进行视频添加水印的&#xff0c;可以图片添加水印。 您可以在视频点播中进行配置&#xff1a; https://help.aliyun.com/zh/vod/user-guide/video-watermarks?spma2c4g.11186623.0.i2 原来的业务代码都是使用python 对oss的 视频进行上传 的,上传的视频路径已经保存到…

提高效率与寿命:坚持正确的码垛机器人操作流程

在工业生产中&#xff0c;码垛机器人以其提升效率、减少损耗、降低成本等优势&#xff0c;成为众多行业不可或缺的重要设备。然而&#xff0c;与所有精密机械一样&#xff0c;码垛机器人的使用寿命很大程度上取决于正确的操作流程。科学规范的操作不仅保障生产顺利进行&#xf…

关于 Microsoft Visual Studio

关于 Microsoft Visual Studio References References [1] Yongqiang Cheng, https://yongqiang.blog.csdn.net/

EPO企业生产运营数智化平台助力制造企业迈向智能制造

随着“中国制造2025”和工业4.0的不断推进&#xff0c;越来越多的制造企业准备迈入智能制造和智慧制造领域&#xff0c;实现数智化管理。企业通过搭建EPO企业生产运营平台&#xff0c;结合自身业务现状和数字化需求&#xff0c;从各个业务场景、部门人员、产品组成等方面进行分…

XSS一-WEB攻防-XSS跨站反射型存储型DOM型标签闭合输入输出JS代码解析

演示案例&#xff1a; XSS跨站-输入输出-原理&分类&闭合XSS跨站-分类测试-反射&存储&DOM #XSS跨站-输入输出-原理&分类&闭合 漏洞原理&#xff1a;接受输入数据&#xff0c;输出显示数据后解析执行 基础类型&#xff1a;反射(非持续)&#xff0c;存储(…

内网渗透(二)必须了解Windows域环境

★★免责声明★★ 文章中涉及的程序(方法)可能带有攻击性&#xff0c;仅供安全研究与学习之用&#xff0c;读者将信息做其他用途&#xff0c;由Ta承担全部法律及连带责任&#xff0c;文章作者不承担任何法律及连带责任。 1、Windows域环境简介 Windows域是计算机网络的一种形式…

鸿蒙一次开发,多端部署(十二)资源使用

在页面开发过程中&#xff0c;经常需要用到颜色、字体、间距、图片等资源&#xff0c;在不同的设备或配置中&#xff0c;这些资源的值可能不同。有两种方式处理&#xff1a; 应用资源&#xff1a;借助资源文件能力&#xff0c;开发者在应用中自定义资源&#xff0c;自行管理这些…

知识管理入门:轻松选择合适的知识管理软件

你是不是经常觉得自己的大脑像个杂乱的仓库&#xff0c;各种信息、知识和想法在里面乱窜&#xff0c;找不到头绪&#xff1f;别担心&#xff0c;知识管理软件来帮你解决这个问题啦&#xff01;今天&#xff0c;我们就来聊聊知识管理软件这个神奇的工具&#xff0c;新手也能轻松…

MongoDB知识

1、部署MongoDB &#xff08;1&#xff09;new好一个mongo文件之后执行 &#xff08;出现mongodb.key&#xff09;记得放行端口 openssl rand -base64 666 > mongodb.key &#xff08;2&#xff09;放到一个docker-compose.yml之后docker-compose up -d执行 version: 3.…

网络安全实训Day11

写在前面 IPSec来喽。有时候把xmind直接粘贴过来会有顺序错位的情况&#xff0c;又被气晕 网络安全实训-网络安全技术 IPSec VPN IPSec 用于保障IP协议安全性的技术 相关概念 工作模式 传输模式&#xff1a;只对数据提供安全保护&#xff0c;不封装公网头部 隧道模式&#…

leetcode 239.滑动窗口最大值

题目 思路 这是单调队列的经典题目。 最基本思路就是&#xff08;拿窗口大小为3说明&#xff09;&#xff1a; 从队列中已经有三个元素开始。先要pop掉第一个元素&#xff0c;然后再push进新的元素&#xff0c;最后返回这三个元素中最大的那一个。 pop和push操作都很简单&a…

正弦实时数据库(SinRTDB)的部署架构

为了适应各种类型的项目需求&#xff0c;正弦实时数据库支持单机部署、双主高可用集群部署、混合部署模式&#xff0c;各部署模式的特点如下&#xff1a; 单机部署 应用于风力发电、光伏、火电 等厂、站级监控系统。支持组态、实时计算、报警服务、报表统计等业务。 双主高可…

jmeter断言使用方法

断言主流的有两种&#xff1a;响应断言、JSON断言 响应断言 1、http请求添加响应断言 2、三种作用域&#xff1a;第一种既作用主请求又作用子请求、只作用主请求、只作用子请求。我们默认选中间的仅作用主请求即可。 3、测试字段和匹配规则 测试字段一般选择响应文本即可&am…
最新文章