使用场景:当你需要一个有固定元素个数的序列,class array<>性能最佳。因为内存被分配在stack中(如果可能的话),绝不会被重新分
什么是array
std::array
是封装固定大小数组的容器。- 一个
std::array
实体,相当于 C-style array身上包裹了一个有用的class。为什么要这么做呢?- 是为了提供STL容器的接口。
- 不同于 C 风格数组,它不会自动退化成 T* 。因此,它比寻常的array安全,而且效率也没有因此变差
- 注意:当其长度为零时
array
(N == 0
)有特殊情况。此时, array.begin() == array.end() ,并拥有某个唯一值。在零长array
上调用 front() 或 back() 是未定义的。
怎么用
Array的能力
初始化
// 用聚合初始化构造
std::array<int, 3> a1{ {1, 2, 3} }; // CWG 1270 重申前的 C++11 中要求双花括号
// ( C++11 之后的版本和 C++14 起不要求)
std::array<int, 3> a2 = {1, 2, 3}; // = 后决不要求
std::array<std::string, 2> a3 = { std::string("a"), "b" };
还可以使用to_array构建数组
#include <type_traits>
#include <utility>
#include <array>
#include <memory>
int main()
{
// 复制字符串字面量
auto a1 = std::to_array("foo");
static_assert(a1.size() == 4);
// 推导元素类型和长度
auto a2 = std::to_array({ 0, 2, 1, 3 });
static_assert(std::is_same_v<decltype(a2), std::array<int, 4>>);
// 推导长度而元素类型指定
// 发生隐式转换
auto a3 = std::to_array<long>({ 0, 1, 3 });
static_assert(std::is_same_v<decltype(a3), std::array<long, 3>>);
auto a4 = std::to_array<std::pair<int, float>>(
{ { 3, .0f }, { 4, .1f }, { 4, .1e23f } });
static_assert(a4.size() == 3);
// 创建不可复制的 std::array
auto a5 = std::to_array({ std::make_unique<int>(3) });
static_assert(a5.size() == 1);
// 错误:不支持复制多维数组
// char s[2][6] = { "nice", "thing" };
// auto a6 = std::to_array(s);
}
swap和move语义
#include <algorithm>
#include <iostream>
#include <array>
int main()
{
std::array<int, 3> alice{1, 2, 3};
std::array<int, 3> bob{7, 8, 9};
auto print = [](const int& n) { std::cout << " " << n; };
// 打印交换前的状态
std::cout << "alice:";
std::for_each(alice.begin(), alice.end(), print);
std::cout << '\n';
std::cout << "bob :";
std::for_each(bob.begin(), bob.end(), print);
std::cout << '\n';
std::cout << "-- SWAP\n";
std::swap(alice,bob);
// 打印交换后的状态
std::cout << "alice:";
std::for_each(alice.begin(), alice.end(), print);
std::cout << '\n';
std::cout << "bob :";
std::for_each(bob.begin(), bob.end(), print);
std::cout << '\n';
}
大小(size)
Array的操作
建立/复制/销毁(create、copy、destory)
非更易型操作
std::size
作用: 返回给定容器 c
或数组 array
的大小
#include <iostream>
#include <vector>
#include <iterator>
int main()
{
std::vector<int> v = { 3, 1, 4 };
std::cout << std::size(v) << '\n';
int a[] = { -5, 10, 15 };
std::cout << std::size(a) << '\n';
}
std::empty
作用:返回给定的容器是否为空
#include <iostream>
#include <vector>
template <class T>
void print(const T& container)
{
if ( !std::empty(container) )
{
std::cout << "Elements:\n";
for ( const auto& element : container )
std::cout << element << '\n';
}
else
{
std::cout << "Empty\n";
}
}
int main()
{
std::vector<int> c = { 1, 2, 3 };
print(c);
c.clear();
print(c);
int array[] = { 4, 5, 6 };
print(array);
auto il = { 7, 8, 9 };
print(il);
}
按照字典顺序比较array
#include <iostream>
#include <array>
int main()
{
std::array<int, 3> alice{1, 2, 3};
std::array<int, 3> bob{7, 8, 9};
std::array<int, 3> eve{1, 2, 3};
std::cout << std::boolalpha;
// 比较不相等的容器
std::cout << "alice == bob returns " << (alice == bob) << '\n';
std::cout << "alice != bob returns " << (alice != bob) << '\n';
std::cout << "alice < bob returns " << (alice < bob) << '\n';
std::cout << "alice <= bob returns " << (alice <= bob) << '\n';
std::cout << "alice > bob returns " << (alice > bob) << '\n';
std::cout << "alice >= bob returns " << (alice >= bob) << '\n';
std::cout << '\n';
// 比较相等的容器
std::cout << "alice == eve returns " << (alice == eve) << '\n';
std::cout << "alice != eve returns " << (alice != eve) << '\n';
std::cout << "alice < eve returns " << (alice < eve) << '\n';
std::cout << "alice <= eve returns " << (alice <= eve) << '\n';
std::cout << "alice > eve returns " << (alice > eve) << '\n';
std::cout << "alice >= eve returns " << (alice >= eve) << '\n';
}
赋值操作
元素访问
std::array::operator[]
作用:返回位于指定位置 pos
的元素的引用。不进行边界检查
#include <array>
#include <iostream>
int main()
{
std::array<int,4> numbers {2, 4, 6, 8};
std::cout << "Second element: " << numbers[1] << '\n';
numbers[0] = 5;
std::cout << "All numbers:";
for (auto i : numbers) {
std::cout << ' ' << i;
}
std::cout << '\n';
}
std::array::at
作用:返回位于指定位置 pos
的元素的引用,有边界检查。若 pos
不在容器范围内,则抛出 std::out_of_range 类型的异常。
#include <iostream>
#include <array>
int main()
{
std::array<int,6> data = { 1, 2, 4, 5, 5, 6 };
// Set element 1
data.at(1) = 88;
// Read element 2
std::cout << "Element at index 2 has value " << data.at(2) << '\n';
std::cout << "data size = " << data.size() << '\n';
try {
// Set element 6
data.at(6) = 666;
} catch (std::out_of_range const& exc) {
std::cout << exc.what() << '\n';
}
// Print final values
std::cout << "data:";
for (int elem : data)
std::cout << " " << elem;
std::cout << '\n';
}
std::array::front、std::array::back
#include <array>
#include <iostream>
int main()
{
std::array<char, 6> letters {'o', 'm', 'g', 'w', 't', 'f'};
if (!letters.empty()) {
std::cout << "The first character is: " << letters.front() << '\n';
std::cout << "The last character is: " << letters.back() << '\n';
}
}
迭代器
把array当做C-Style Array
异常
Tuple接口
std::get(std::array)
作用: 从 array
提取第 x 个元素。
#include <iostream>
#include <array>
int main()
{
std::array<int, 3> arr;
// 设置值:
std::get<0>(arr) = 1;
std::get<1>(arr) = 2;
std::get<2>(arr) = 3;
// 获取值:
std::cout << "(" << std::get<0>(arr) << ", " << std::get<1>(arr)
<< ", " << std::get<2>(arr) << ")\n";
}
两个疑问
- 为什么要引入std::array而不是直接使用std::vector
- 已经有了传统数组,为什么还要std::array
第一个问题:与std::vector不同,std::array对象的大小是固定的,如果容器大小是固定的话,应该优先使用std::array。另外由于std::vector是自动扩容的,当存入大量的数据之后,并且对容器进行了删除操作,容器并不会自动归还被删除元素的内存,这时候就需要手动运行shrink_to_fit()释放这部分内存:
#include <iostream>
#include <vector>
int main(){
std::vector<int> v;
printf("size = %d\n", v.size()); //输出0
printf("capacity = %d\n", v.capacity()); //输出0
//std::vector的存储是自动管理的,按需自动扩张。但是如果空间不足,
// 需要重新分配更多的内存,而重分配内存通常是性能上有所开销
v.push_back(1);
v.push_back(2);
v.push_back(3);
printf("size = %d\n", v.size()); //输出3
printf("capacity = %d\n", v.capacity()); //输出4
v.push_back(4);
v.push_back(5);
printf("size = %d\n", v.size()); //输出5
printf("capacity = %d\n", v.capacity()); //输出8
// 如下可以看出容器虽然清空了元素,但是被清空元素的内存并没有归还
v.clear();
printf("size = %d\n", v.size()); //输出0
printf("capacity = %d\n", v.capacity()); //输出8
// 额外内存可以通过shrink_to_fit()调用返回给系统
v.shrink_to_fit();
printf("size = %d\n", v.size()); //输出0
printf("capacity = %d\n", v.capacity()); //输出0
return 0;
}
第二个问题更简单,使用std::array能够让代码更加”现代化“,而且封装了一些操作函数,比如获取数组大小以及检查是否非空等,同时还能友好的使用标准库中的容器算法
std::find
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
int main()
{
std::vector<int> v = { 3, 1, 4 };
if (std::find(std::begin(v), std::end(v), 5) != std::end(v)) {
std::cout << "found a 5 in vector v!\n";
}
int a[] = { 5, 10, 15 };
if (std::find(std::begin(a), std::end(a), 5) != std::end(a)) {
std::cout << "found a 5 in array a!\n";
}
}
其他例子
一维数组
#include <iostream>
#include <array>
using namespace std;
void main()
{
array<int, 10>myint{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; //栈上数组,内存连续
//array<int, 100000>myint{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; 判断一个数组是不是在栈上,看是不是栈溢出
for (auto i : myint)
{
cout << i << " " << (void *)&i << " " << &myint << " " << &myint[i] << endl; //副本i机制,所以全部是数组的首地址
}
//(void *)&i和&myint地址不一样,说明副本机制,&i和&myint分别是i和myint数组的首地址
cout << "------查看数组地址-----------" << endl;
for (int i = 0; i < 10; i++)
{
cout << myint[i] << " " << (void*)&myint[i] << " " << &myint[i] << endl;
}
cin.get();
}
总结:
1、for和auto结合,是一个迭代器
2、CPP中数组初始化的库#include <array>
3、array<int, 10>myint{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}; //栈上数组,内存连续
二维数组
#include <iostream>
#include <array>
using namespace std;
void main()
{
cout << "********二维数组***********" << endl;
array<int, 10>myint1{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
array<int, 10>myint2{ 11, 22, 33, 44, 54, 6, 7, 8, 9, 0 };
array<int, 10>myint3{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
array<int, 10>myint4{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
array<array<int, 10>, 4>myintw{ myint1, myint2, myint3, myint4 };
for (auto j : myintw)
{
// cout << j << " "; // E0349 没有与这些操作数匹配的 "<<" 运算符
for (auto i : j)
{
cout << i << " ";
}
cout << "\n";
}
cout << "\n\n";
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 10; j++)
{
cout << myintw[i][j] << " ";
}
cout << "\n";
}
cin.get();
}