编程语言
首页 > 编程语言> > 跟我学C++中级篇——STL的容器Array

跟我学C++中级篇——STL的容器Array

作者:互联网

 

一、顺序容器Array

STL中的Array数组类型是在c++ TR1中才提出的,在之前只有Vector这个类似于数组的类型。但在实际应用中发现,vector和实际应用数组还是有非常大的区别,包括迭代器访问的控制,内存大小的控制等。用过vector的很容易发现它和实际使用中的数组的诸多不同之处。
换句话说,实际开发过程中,还是需要一个和数组高度类似的数据类型,这也是std::array的出现的一个原因,正如军事上的火力配比一样,不能出现火力空白区。那么这二者最关键的不同在哪儿呢?有两个主要方面,第一是array的内存是在栈上,而vector是在堆上。它的直接结果就是理论上讲,array的大小有上限(默认WIN是1M,LINUX是10M,不过可以动态调整),而vector理论上讲可以认为上限非常大(X64平台上,但实际仍受限于物理内存和OS系统)。同时栈和堆又导致了内存生命周期的不同,应用范围的不同等等。而栈的有限性又显示标注了array的固定大小性。第二个就是为了保持与C数组的最大的类似,其构造函数、析构函数和赋值操作符等都是隐式声明的,这也是一个从设计原理上与vector不同的地方。

二、源码分析

其源码定义为:

template <class _Ty, size_t _Size>
class array {  // fixed size array of values
public:
    using value_type      = _Ty;
    using size_type       = size_t;
    using difference_type = ptrdiff_t;
    using pointer         = _Ty*;
    using const_pointer   = const _Ty*;
    using reference       = _Ty&;
    using const_reference = const _Ty&;

    using iterator       = _Array_iterator<_Ty, _Size>;
    using const_iterator = _Array_const_iterator<_Ty, _Size>;

    using reverse_iterator       = _STD reverse_iterator<iterator>;
    using const_reverse_iterator = _STD reverse_iterator<const_iterator>;

#if _HAS_TR1_NAMESPACE
    _DEPRECATE_TR1_NAMESPACE void assign(const _Ty& _Value) {
        _STD fill_n(_Elems, _Size, _Value);
    }
#endif // _HAS_TR1_NAMESPACE

    void fill(const _Ty& _Value) {
        _STD fill_n(_Elems, _Size, _Value);
    }

    void swap(array& _Other) noexcept(_Is_nothrow_swappable<_Ty>::value) {
        _Swap_ranges_unchecked(_Elems, _Elems + _Size, _Other._Elems);
    }

    _NODISCARD _CONSTEXPR17 iterator begin() noexcept {
        return iterator(_Elems, 0);
    }

    _NODISCARD _CONSTEXPR17 const_iterator begin() const noexcept {
        return const_iterator(_Elems, 0);
    }

    _NODISCARD _CONSTEXPR17 iterator end() noexcept {
        return iterator(_Elems, _Size);
    }
    ......
}

Array的源码定义不复杂,就是一个带有非模板类型参数的模块类。非模板类型用于指定整个模板数组的大小,使用size_t来定义。类的开始使用using重定义了一大批的类型,供后面相关代码使用。在这个模板类中重载发[]运算符并实现了at函数,这两个函数返回的是一个当前元素的引用。
另外一个需要说明的是,在这个类模板中,使用的是隐式声明的构造函数和析构函数,它的定义符合聚合初始化的规则来初始化数组。
在Array中,经历了版本的不断的迭代,从c++11到C++17再到最新的c++20,反正都是让人越来越好用的方法。这会在下面的例程中进行体现。

三、例程

看几个例子:
1、声明定义和使用

void TestArray()
{
    std::array<int, 20> arr = {0};  //标准用法
    std::array arr17 = {1,2,3,4,5,6,7};  //C++17自动推导
    auto arr20 = {23,23,32,32,36,36};
}

2、生成一个array,std::to_array

在实际开发中,C数组在应用中经常退化为指针来使用,这也是初学者在实际开发中,对指针比较恐惧的一个原因之一,指针为啥莫名其妙又变成了数组,特别是涉及到高维数组,老程序员也会出现短暂的理解认知时间。
目前在cppreference上定义的介绍为:

namespace detail {
template <class T, std::size_t N, std::size_t... I>
constexpr std::array<std::remove_cv_t<T>, N>
    to_array_impl(T (&a)[N], std::index_sequence<I...>)
{
    return { {a[I]...} };
}
}

template <class T, std::size_t N>
constexpr std::array<std::remove_cv_t<T>, N> to_array(T (&a)[N])
{
    //make_index_sequence就是为了节省数组长度构造的方法,否则1000个甚至更多怎么遍历?
    return detail::to_array_impl(a, std::make_index_sequence<N>{});
}

它的应用方法如下:

void TestArray()
{
  //这两行代码,禁止隐匿转换,否则报错
    auto arr2x = std::to_array< int>({1,3,5,7,9});
  //这行,会服一个警告,最好在to_array中给出大小,即注释那行
  //std::array<unsigned int, 3> a20 = std::to_array<unsigned int,3>({1,3,5});
    std::array<unsigned int, 3> a20 = std::to_array<unsigned int>({1,3,5});

    for (int num = 0; num < a20.size(); num++)
    {
        std::cout<<"std array value:" << a20[num] << std::endl;
    }
}

这里需要说明的是,一定要显示的使用类型,保证数据安全,这也是C/C++安全开发的一个要求。

3、做为返回值返回
在实际开发中,如果在栈区上有一个数组,一般是严格禁止做为返回值的形式酆的。在函数中返回一个栈上的值,是非常危险的,但是std::array的出现则实现了这种操作的安全性。

std::array<int,3> GetArray()
{
    std::array<int, 3> arr = {1,2,3};
    return arr;
}
int* GetCarr()
{
    int buf[3] = {3,2,1};
    return buf;
}
void TGetArray()
{
  //此处调用,操作p,是不可预知的,特别是在复杂的线程环境中
    int* p = GetCarr();

  //这个是安全的
    auto arr = GetArray();
    for (int num = 0; num < arr.size(); num++)
    {
        std::cout << "std array value:" << arr[num] << std::endl;
    }
}

比较主要的三种用法,可以自己试试。具体的操作函数操作,非常简单,这里就不再一一举例了。

四、总结

其实对于标准制定者来说,是非常左右为难的,既要兼顾易用性,又要兼顾容易理解性,还得照顾各种历史的传承,所以同学们应该明白为啥新语言一出来,就很容易被高手们一下子看到其开发的本质就在于此。因为没有历史的包袱,所以新语言能创造性的使用一些新技术新技巧,从而在某个方面迅速展露头角,(当然,也有搞不好的)但是随着时间的推进,版本的迭代,同样会变得越来越臃肿,这也是没办法的办法。

 

标签:std,跟我学,const,iterator,STL,C++,数组,using,array
来源: https://blog.csdn.net/fpcc/article/details/113869127