行行宜行行

热爱风光、热爱新技术、踏出每一步,过好每一天!

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

 

Tags:

发表回复

Your email address will not be published. Required fields are marked *.

*
*

近期评论

您尚未收到任何评论。

conviction

要想看到璀璨的日出时刻,就要有跋山涉水的决心和坚毅