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

【C++】列表初始化、声明、范围for、array容器

列表初始化、声明、范围for、array容器

    • 一、统一的列表初始化
      • 1.1 使用{ }初始化
      • 1.2 initializer_list容器
    • 二、声明
      • 2.1 auto关键字
      • 2.2 decltype关键字
      • 2.3 nullptr关键字
    • 三、范围for
    • 四、array容器和forward_list容器

一、统一的列表初始化

1.1 使用{ }初始化

在C++98中,标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定。比如:

struct Person
{string _nameint _age;
};
int main()
{int arr1[] = { 1, 2, 3, 4, 5 };int arr2[5] = { 0 };Person p = { "张三", 21};return 0;
}

C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用初始化列表时,可添加等号(=),也可不添加。

struct Person
{string _nameint _age;
};
int main()
{//单个元素int a = 1;int b = { 2 };int c{ 2 };//数组int arr1[]{ 1, 2, 3, 4, 5 };int arr2[5]{ 0 };//自定义结构体Person p = { "张三", 21};//调用new时,对每个对象初始化int* p1 = new int(1);int* p2 = new int[3]{ 1, 3, 4 };return 0;
}

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

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(2024, 12, 17); // old style// C++11支持的列表初始化,这里会调用构造函数初始化Date d2 = { 2024, 12, 17 };//类似隐式类型转换 + 优化Date d3{ 2024, 12, 17 };Date* p1 = new Date(2024, 12, 17);Date* p2 = new Date[3]{ { 2024, 12, 17}, { 2024, 12, 18}, { 2024, 12, 19} };return 0;
}

1.2 initializer_list容器

c++11里添加了initializer_list容器,介绍文档链接:initializer_list的介绍

此容器提供的成员函数只有三个,还有一个构造函数:

在这里插入图片描述

int main()
{initializer_list<double> lt = { 24, 17, 21 };initializer_list<double>::iterator it = lt.begin();while (it != lt.end()){cout << *it << " ";//24 17 21it++;}cout << endl;for (auto e : lt){cout << e << " ";//24 17 21}return 0;
}

该类型用于访问C++初始化列表中的值,该列表是 类型的元素列表。这种类型的对象由编译器从初始化列表声明自动构造,初始化列表声明是用大括号括起来的逗号分隔元素的列表:const T。常量的花括号列表会被编译器识别成initializer_list

int main()
{// the type of il is an initializer_listauto il = { 10, 20, 30 };cout << typeid(il).name() << endl;//class std::initializer_list<int>return 0;
}

initializer_list的使用场景:

initializer_list一般是作为构造函数的参数,C++11对STL中的不少容器增加initializer_list作为参数的构造函数,这样初始化容器对象时就变得方便了,也可以作为operator =的参数,这样就可以用大括号赋值:

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()
{//vectorvector<int> v1 = { 1, 2 ,3 ,4, 5 };// 使用大括号对容器赋值,{}调用构造函数构造一个vector对象,再赋值v1 = {10, 20, 30};vector<int> v2{ 1, 2, 3, 4, 5 };vector<Date> v3 = { { 2024, 12, 17}, { 2024, 12, 18}, { 2024, 12, 19} };//listlist<int> lt1{ 1, 2, 3 };//setset<int> s1{ 3, 4, 5, 6, 3 };//mapmap<string, string> dict = { {"apple", "苹果" }, {"english", "英语" } };return 0;
}

initializer_list的使用示例:

对我们以前实现过的vector类型中的构造函数进行改动,可以使构造函数还能实现的再简单点,直接在初始化列表里把三个成员变量初始化,在遍历ls,复用push_back依次插入到vector即可:

vector(initializer_list<T> il):_start(nullptr), _finish(nullptr), _endofstoage(nullptr)
{for (auto e : il){push_back(e);}
}

注意:

  • 最好增加一个以initializer_list作为参数的赋值运算符重载函数,以支持直接用列表对容器对象进行赋值,但实际也可以不增加。

如下:

vector<int> v1 = { 1, 2 ,3 ,4, 5 };
// 使用大括号对容器赋值,{}调用构造函数构造一个vector对象,再赋值
v1 = {10, 20, 30};

对于第二行的赋值操作,涉及到了隐式类型转换,先使用{}调用构造函数构造一个vector对象,再赋值。

二、声明

c++11提供了多种简化声明的方式,尤其是在使用模板时。

2.1 auto关键字

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

int main()
{int i = 10;//int*auto p = &i;map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };//map<string, string>::iteratorauto it = dict.begin();return 0;
}

2.2 decltype关键字

decltype 是 C++11 引入的一个关键字,用于在编译时推导表达式的类型。它根据给定表达式的类型来确定一个新类型,可以在模板编程和类型推导中非常有用

// decltype的一些使用使用场景
template<class T1, class T2>
void F(T1 t1, T2 t2)
{decltype(t1 * t2) ret;cout << typeid(ret).name() << endl;//double
}
int main()
{const int x = 1;double y = 2.2;decltype(x * y) ret; // ret的类型是doubledecltype(&x) p; // p的类型是int const *cout << typeid(ret).name() << endl;//doublecout << typeid(p).name() << endl;//int const *F(1, 'a');return 0;
}

下面来区分下typeid和decltype:

  • typeid(变量名).name():专门用来输出一个变量的类型,返回的是一个字符串。帮助我们观察此字符串的类型,不能用其去定义变量。
  • decltype:将变量的类型声明为表达式指定的类型,可以用其去定义变量。

2.3 nullptr关键字

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

#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endi

如果没有定义宏,如果在cplusplus里,NULL被定义成0。可以看到,NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量。不论采取何种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦,比如:

void print(int* a){cout << "int*" << endl;
}
void print(int a){cout << "int" << endl;
}int main(){print(NULL); //intreturn 0;
}

程序本意是想通过print(NULL)调用指针版本的print(int *)函数,但是由于NULL被定义成0,因此与程序的初衷相悖。在C++98中,字面常量0既可以是一个整形数字,也可以是无类型的指针(void)常量,但是编译器默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转(void *)0。

注意:

  1. 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的。
  2. 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
  3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。

三、范围for

范围for的底层就是被替换成了迭代器。

范围for的语法

若是在C++98中我们要遍历一个数组,可以按照以下方式:

int main()
{int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//将数组元素值全部乘以2for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){arr[i] *= 2;}//打印数组中的所有元素for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){cout << arr[i] << " ";}cout << endl;return 0;
}

以上方式也是C语言中所用的遍历数组的方式,但对于一个有范围的集合而言,循环是多余的,有时还容易犯错。

C++11中引入了基于范围的for循环,for循环后的括号由冒号分为两部分,第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。比如

int main()
{int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//将数组元素值全部乘以2for (auto& e : arr){e *= 2;}//打印数组中的所有元素for (const auto e : arr){cout << e << " "; // 2 4 5 8 10 12 14 16 18 20}cout << endl;return 0;
}

注意: 与普通循环类似,可用continue来结束本次循环,也可以用break来跳出整个循环。

范围for的使用条件

1. for循环迭代的范围必须是确定的

  • 对于数组而言,就是数组中第一个元素和最后一个元素的范围;对于类而言,应该提供begin和end的方法,begin和end就是for循环迭代的范围。

2. 迭代的对象要支持++和==操作

  • 范围for本质上是由迭代器支持的,在代码编译的时候,编译器会自动将范围for替换为迭代器的形式。而由于在使用迭代器遍历时需要对对象进行++和操作,因此使用范围for的对象也需要支持++和操作。

四、array容器和forward_list容器

1、array容器

array就是一个静态数组,其有两个模板参数,第一个模板参数代表的是存储的数据类型,第二个是非类型模板参数,代表的是存储元素的个数:

int main()
{array<int, 10> a1;array<double, 15> a2;return 0;
}

array和普通数组最大的区别在于对于越界访问的检查:

int main()
{int a[10];cout << a[10] << endl;//越界不一定能检查出来array<int, 10> b;cout << b[10] << endl;//只要越界,一定能检查出来return 0;
}

总结:

  1. array容器的对象是建立在栈区的,不适合定义大数组
  2. array容器的设计可能是为了代替静态数组,因为array容器更安全,能够检查除越界的错误,而静态数组并不一定能够检查出来。

2、forward_list容器

forward_list容器本质就是一个单链表,相比list的区别在于forward_list节省了空间,实际使用上使用forward_list的比率还是比较低的,还是使用list来的方便。

容器中的一些新方法

如果我们再细细去看会发现基本每个容器中都增加了一些C++11的方法,但是其实很多都是用得比较少的。

比如提供了cbegin和cend方法返回const迭代器等等,但是实际意义不大,因为begin和end也是可以返回const迭代器的,这些都是属于锦上添花的操作。

http://www.lryc.cn/news/505255.html

相关文章:

  • C++智能指针详解
  • 基础库正则表达式
  • 【spring专题】spring如何解析配置类和扫描包路径
  • MyBatis框架的入门
  • 代码随想录D22-23 回溯算法01-02 Python
  • 【网络云计算】2024第50周-每日【2024/12/13】小测-理论-写10个Bash Shell脚本-解析
  • MATLAB转换C语言--问题(一)FFT 和 IFFT 的缩放因子
  • 轻松上手:使用 Vercel 部署 HTML 页面教程
  • 如何运用 HTM?
  • 12.16【net】【study】
  • 2023和2024历年美赛数学建模赛题,算法模型分析!
  • Node.js内置模块
  • 测评|携程集团25年社招在线测评北森题库、真题分析、考试攻略
  • 快速启动Go-Admin(Gin + Vue3 + Element UI)脚手架管理系统
  • 数据分流:优化数据处理流程的关键策略
  • RabbitMQ如何构建集群?
  • RNN LSTM Seq2Seq Attention
  • 硬件设计-ADC和低本底噪声为何至关重要
  • 个性化域名配置
  • uniapp中打包应用后,组件在微信小程序和其他平台实现不同的样式
  • MRI脑肿瘤检测数据集,使用500张原始图片标注,支持yolo,coco,voc格式
  • JumpServer开源堡垒机搭建及使用
  • Java 编程旅程(二)
  • 一、springcloud 入门——笔记
  • 思考:VSCode 的宏观工作原理 快速入门 VSCodium (****)
  • C++ day8——模版
  • 【CSS in Depth 2 精译_080】 13.1:CSS 渐变效果(中)——不同色彩空间的颜色插值算法在 CSS 渐变中的应用
  • 红日靶场1(搭建打靶)
  • LivePortrait 部署笔记
  • Greenhills Lib操作-查看Lib信息与将lib中的data段link到指定区域