***🎬慕斯主页***:*******修仙—别有洞天******** ♈️*今日夜电波:マイノリティ脈絡—ずっと真夜中でいいのに。* 0:24━━━━━━️💟──────── 4:02 🔄 ◀️ ⏸ ▶️ ☰ 💗关注👍点赞🙌收藏*您的每一次鼓励都是对我莫大的支持*😍
统一的列表初始化
在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(这里只举几个例子)。这也使得所有的容器都可使用花括号进行初始化:![](https://img-blog.csdnimg.cn/img_convert/1c9a096925c16e975f10f5fbb252e7e1.png)
我们像如下对容器使用{ }:
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::array
的at()
成员函数在索引越界时会抛出异常,而内置数组则可能导致未定义行为。- 更好的接口:
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
你可以使用花括号初始化列表或
insert
、
emplace
等成员函数来创建和填充
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
可能比list
或vector
等其他容器更适合,特别是在你需要一个简单的单向链表并且空间效率很重要的时候。但是,由于它只支持单向遍历,因此在某些算法上可能不如双向链表或数组高效。
** 感谢你耐心的看到这里ღ( ´・ᴗ・` )比心,如有哪里有错误请踢一脚作者o(╥﹏╥)o!**
![](https://img-blog.csdnimg.cn/a2296f4aa7fd45e9b1a1c44f9b8432a6.gif)
** 给个三连再走嘛~ **
版权归原作者 慕斯( ˘▽˘)っ 所有, 如有侵权,请联系我们删除。