std::array原理分析与实现
介绍
固定大小的数组顺序容器,是对原始 C 风格数组的封装。它提供了 STL 容器的接口(如迭代器、算法支持等),但其底层仍是静态分配的数组,大小在编译期就确定。
#include <array>
std::array<int, 5> arr = {1, 2, 3, 4, 5};
特点
- 由于底层是静态分配的数组,并且在定义时传入对应的数组大小,所以存储在栈上,不适用动态内存;
- 长度在编译期固定,不可更改;
- 支持完整的STL接口:迭代器、
at()
、begin()
、end()
、fill()
、swap()
、与算法库配合使用
全部接口
函数名 | 作用 |
---|---|
operator[] |
不检查边界访问元素,不安全,需要自己手动检查边界 |
at(index) |
检查边界访问元素,越界抛异常 |
front() |
获取数组第一个元素 |
back() |
获取数组最后一个元素 |
data() |
返回原始指针 |
size() |
返回数组大小(编译期常量 N) |
fill(val) |
用 val 填充所有元素 |
swap(other) |
与另一个数组交换内容 |
begin() /end() |
获取迭代器,头迭代器与尾后迭代器 |
rbegin() /rend() |
反向迭代器 |
std::get<k>() |
编译期访问第 k 个元素(支持结构化绑定),这里的k是索引,0 <= k < N |
结构化绑定
在C++17中,与tuple类似,可以结构化绑定,返回多值
std::array<int, 2> pair = {10, 20};
auto [x, y] = pair;
std::array<int, 2> func() {
return {5, 6};
}
自定义Array
自己手写了array
,实现了所有的std::array
的接口及其函数
#pragma once
#include "MyIterator.h"
template <typename T, size_t N>
struct MyArray
{
using value_type = T;
using size_type = size_t;
using difference_type = ptrdiff_t;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = value_type*;
using const_pointer = const value_type*;
// 以下是迭代器
using iterator = MyIterator<value_type>;
using const_iterator = MyIterator<const value_type>;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
// 本质是固定大小的数组
value_type elems[N];
// Array要求所有的接口都在编译器计算得到结果
// 重载[]
constexpr reference operator[](size_t index)
{
return elems[index];
}
constexpr const_reference operator[](size_t index) const
{
return elems[index];
}
// 找到头元素和尾巴元素
constexpr reference front()
{
return elems[0];
}
constexpr const_reference front() const
{
return elems[0];
}
constexpr reference back()
{
return elems[N - 1];
}
constexpr const_reference back() const
{
return elems[N - 1];
}
// 获取当前指针
constexpr pointer data() noexcept
{
return elems;
}
constexpr const_pointer data() const noexcept
{
return elems;
}
// 获取迭代器 begin-cbegin
constexpr iterator begin() noexcept
{
return iterator(elems);
}
constexpr const_iterator begin() const noexcept
{
return const_iterator(elems);
}
constexpr const_iterator cbegin() const noexcept
{
return const_iterator(elems);
}
constexpr iterator end() noexcept
{
return iterator(elems + N);
}
constexpr const_iterator end() const noexcept
{
return const_iterator(elems + N);
}
constexpr const_iterator cend() const noexcept
{
return const_iterator(elems + N);
}
// 以下是反向迭代器
constexpr reverse_iterator rbegin() noexcept
{
return reverse_iterator(end());
}
constexpr const_reverse_iterator rbegin() const noexcept
{
return const_reverse_iterator(end());
}
constexpr reverse_iterator rend() noexcept
{
return reverse_iterator(begin());
}
constexpr const_reverse_iterator rend() const noexcept
{
return const_reverse_iterator(begin());
}
// 以下是一个常用接口
constexpr size_t size() const noexcept
{
return N;
}
constexpr size_t max_size() const noexcept
{
return N;
}
constexpr bool empty() const noexcept
{
return N == 0;
}
// at()
constexpr reference at(size_type idx)
{
// 必须进行边界检查
if (idx >= N) {
throw std::out_of_range("array::at");
}
return elems[idx];
}
constexpr const_reference at(size_type idx) const
{
// 必须进行边界检查
if (idx >= N) {
throw std::out_of_range("array::at");
}
return elems[idx];
}
// fill用value填充
void fill(const T& value)
{
for (size_type i = 0; i < N; i++) {
elems[i] = value;
}
}
// swap
void swap(MyArray& other)
{
for (size_type i = 0; i < N; i++) {
std::swap(elems[i], other.elems[i]);
}
}
// 重载== !=
bool operator==(const MyArray& other) const
{
for (size_type i = 0; i < N; i++) {
if (elems[i] != other.elems[i]) {
return false;
}
}
return true;
}
bool operator != (const MyArray& other) const
{
return !(*this == other);
}
};
[]和at()发生越界访问的不同
[]
- []不做边界检查,可能会导致程序崩溃、数据错误以及内存损坏
- 编译器不会报错,但是运行时崩溃
- 访问了下标以外的元素会发生未定义行为
at()
- 源码实现会做边界检查,安全性更高
- 如果越界,会抛出异常 out_of_range