基于C++11的Array容器分析

Array是C++中的一个静态数组容器,它提供了STL接口,支持随机访问,但大小不可变。Array在初始化时需指定元素类型和数量,且默认构造函数不会初始化元素。Array的元素可以通过下标或成员函数访问,支持迭代器和一些基本的容器操作,如swap和fill。Array的迭代器在swap后仍指向原容器,但指向不同元素。Array还提供了tuple接口,并且在异常处理方面仅在at()函数中进行检查。
摘要由CSDN通过智能技术生成

Array的基本概念

  1. 容器类array<>的一份实体,模塑出一个static array。它包覆一个寻常的static C-style array并提供一个STL容器接口。因此你无法借由增加或移除元素而改变其大小

  1. Class array<>自TR1被引入C++11标准库,其概念是在C-style array上包覆一个有用的class

  1. 使用前提你必须包含头文件<array>

#include<array>
  1. 该类型被定义为一个class template,位于命名空间std中

namespace std{
template<typename T,size_t N>
class array;
}

第一个T为任何被指名的类型,第二个template参数用来指出这个array在其生命周期中拥有的元素个数。Array并不支持指定分配器(allocator)

Array的能力

基于是一个static C-style array的实现,因此Array是一个有序集合,并且支持随机访问,前提是你直到元素位置。Array的迭代器属于随机访问迭代器,所以你可以将任何STL算法运用于它。如果你需要一个有固定元素的序列,class array<>将带来最佳效能。

初始化

对于初始化,class array<>有若干独特语义。第一个例子是,default构造函数并非建立一个空容器。

std::array<int,4>x;   //OOPS:元素可能有未定义的初值

注:array<>是唯一一个“无任何东西被指定为初值时,会被预初始化”的容器,这意味着,初值可能会不明确(因被定义的位置而定),而不是0

你可以为它提供一个空白初值列,这种情况下元素保证被初始化,于是对基础类型而言元素初值为0

std::array<int,4>={ };

由于没有提供针对初值列而写的构造函数或assignment操作符,因此“在array声明期间完成初值化”是使用初值列的唯一途径。基于这个原因,你无法使用小括号语法指明初值(这里不同于其他容器类型)

std::array<int,4> a({1,2,3,4}); //ERROR
std::vector<int,4> c({1,2,3,4}); //OK

Class array<>是个聚合体,这个事实表明,用来保存所有元素的那个成员是public。因此对该public成员的任何直接访问都会导致不可预期的行为,也绝对不具有移植性

swap()和Move语义

和其他所有的容器一样,array<>提供swap()操作。因此你可以和一个相同类型的容器交换(元素类型和元素个数都相同)置换彼此的元素。然而,array<>不能仅仅置换其内部pointer。基于这个原因,swap()用于线性复杂度并带来以下影响:iterator和reference不会随着元素的置换而改变所指的容器。所以置换之后,iterator和reference指向原本容器,但指向至不同的元素。

你可以采用array暗自备妥的move语义。

std::array<std::string,10>as1,as2;
as1=std::move(as2);

大小(Size)

Array大小为0是可能的,那就是个没有任何元素的array。这种情况下bagin()和end(),cbegin()和cend(),以及相应的反向迭代器(reverse iterator)会释放出同一value。然而front()和back()的返回值就不明确了。

std::array<T,0>coll;      //初始化一个空容器
std::sort(coll.begin(),coll.end());   //没错,但是没有效果
coll[5]=elem;   //未定义的行为
std::cout<<coll.front();   //未定义的行为

Array的操作

Class array<>的构造函数

array<T,N>c   //default构造函数,建立一个array带有default-initlist元素
array<T,N>c(c2)  //copy构造函数,建立一个array的拷贝(所有元素都被拷贝)
array<T,N>c=c2   //copy构造函数,建立一个array的拷贝(所有元素都被拷贝)
array<T,N>c(rc)  //move构造函数,取rvalue rv的内容建立一个新的array
array<T,N>c=rv   //move构造函数,取rvalue rv的内容建立一个新的array
array<T,N>c=initlist  //取初值列的元素为初值,建立一个array

注:由于class array<>是一个聚合体,这些构造函数只是被隐式定义出来。你可以建立array并且指定(不指定)元素初值。default构造函数在默认情况下将元素初始化,这意味着基础类型的初值是不确定的。如果你使用一个初值值列表却没有传递足够元素,剩下的的元素会被其default构造函数建立起来(基础类型的初始值为0)。

再次提醒以下,你不能对array的初始值列表使用小括号形式写法:

std::array<int,4>a({1,2,3,4)}; //ERROR

非更易型操作

c.empty() //返回是否容器为空(相当于size()==0但也许较快)
c.size()  //返回元素的数量
c.max_size() //返回元素个数之最大可能量
c1==c2   //返回c1是否等于c2
c1!=c2   //返回c1是否不等于c2
c1>c2    //返回c1是否大于c2
c1<c2    //返回c1是否小于c2
c1>=c2   //返回c1是否大等于c2
c1<=c2   //返回c1是否小等于c2

赋值(Assignment)

c=c2   //将c2的全部元素赋值给c
c=rv   //将rv以move assign方式赋值给c的每个元素
c.fill(val)  //将val赋值给array的每个元素
c1.swap(c2)  //置换c1和c2的数据
swap(c1,c2)  //置换c1和c2的数据

在assignment操作符之外,只可使用fill()赋新值给每一个元素,或者使用swap()对另一个array置换元素值。如果使用=和swap(),两个array必须具有相同类型。其次,swap()不保证具备常量复杂度。事实上,所有这些操作都调用元素类型所提供的assignment操作符

元素访问(Element Access)

c[idx]   //返回索引idx所指的元素(不检查范围) 
c.at()   //返回索引idx所指的元素(检查范围,超出范围会抛出out_of_range异常) 
c.front()//返回第一个元素
c.back() //返回最末尾的元素

欲访问array内所有元素,你必须使用range-based for 循环,或者特定的操作函数或迭代器。此外由于array提供了一个tuple接口,所以你也可以使用get<>()访问某个元素。

注:对于一个array来说,只有当声明其大小为0它才是空的

std::array<T,4>coll;      //有四个执行默认初始化的元素
coll[5]=elem;             //未定义的行为
std::cout<<coll.front();  //正确(因为其含有4个元素)

std::array<T,0>coll2;     //真正意义上的空容器
std::cout<<coll2.front(); //未定义的行为

所以你必须要能够确保operator []合法,或者使用at()

template<typename T>
    void foo(T&coll){
        if(coll.size()>5){
           coll[5]=...;
         }

         coll.at(5);  //访问失败会抛出out_of_range异常
    }

迭代器相关函数(Iterator Function)

c.begin()  //返回一个random-access iterator指向第一元素
c.end()    //返回一个random-access iterator指向末尾元素的下一位置
c.rbegin() //返回一个reverse random-access iterator指向第一元素
c.rend()   //返回一个reverse random-access iterator指向末尾元素的下一位置
c.cbegin() //返回一个const random-access iterator指向第一元素
c.cend()   //返回一个const random-access iterator指向末尾元素的下一位置
c.crbegin()//返回一个const reverse random-access iterator指向第一元素
c.crend()  //返回一个const reverse random-access iterator指向末尾元素的下一位置

对于array而言,begin(),cbegin(),end()和cend()返回的迭代器往往是寻常pointer,这很好,因为array<>内部使用C-style array存放元素,并使用寻常pointer提供random-access迭代器接口。然而你不可以依赖迭代器是寻常pointer"这个事实。(这个以后再出一篇单独讲,挖一个坑)

只要array保持有效,其迭代器也就保持有效。然而不同于其他容器,swap()乃是将新值赋予iterator,reference和pointer指向的元素身上

把array当成C-Style Array

由于array内部static array实现,它意味着,无论何处,只要你可以使用寻常的C-Style array,你就可以使用array<>。例如你可以使用array持有寻常的C-string的数据

std::array<char,4>a;
strcpy(&a[0],"hello world");    //将常量字符串拷贝入array中
printf("%s\n",&a[0]);           //将其中内容打印出来

然而,如果你想直接访问array的元素,你不一定要用表达式&a[0],因为成员函数data()也具备相同用途

std::array<char,41>a;
strcpy(a.data(),"hello world");
printf("%s\n",a.data());

就和static array的使用一样,使用array<>也要谨慎,你必须确保array的大小足够容纳被复制进来的数据,而且如果你把其中的内容当作一个C-string来看,你必须放置一个'\0'元素于末尾。

注:绝对不要以迭代器表现"第一元素的地址"

printf("%s\n",a.begin());    //ERROR
printf("%s\n",a.data());     //OK

异常处理

Array只提供少量逻辑差错检查。C++standard规定“可抛出异常”的成员函数只有at(),它可以说是下标操作符的安全版本。其余被array调用的函数对于异常处理并无特别保证,异常只有可能发生在你copy,move或assign元素值时。swap()有可能抛出异常,因为它执行的是"元素逐次"的置换动作,而那有可能抛出异常。

Tuple接口

Array提供tuple接口。因此你可以通过表达式tuple_size<>::value取得元素数量,用tuple_element<>::type取得特定元素的类型,用get()取得某特定元素。

using FiveStrings=std::array<std::string,5>;
FiveStrings a={"hello ","nico","how","are","you"};
std::tuple_size<FiveStrings>::value;    //5
std::tuple_elememt<1,FiveStrings>::type;//std::string
std::get<1>(a);                         //std::stirng("nice")

Array运用实例

#include<array>
#include<algorithm>
#include<functional>
#include<numeric>
using namespace std;

int main(){
    array<int,10>a={11,22,33,44};
    for(auto it:a)cout<<it;
    cout<<endl;

    a.back()=9999999;
    a[a.size()-2]=42;
    for(auto it:a)cout<<it;
    cout<<endl;

    cout<<"sum:"
        <<accumulate(a.begin(),a.end(),0);
        <<endl;

    transform(a.begin(),a.end(),a.begin(),negate<int>());
    for(auto it:a)cout<<it;
    cout<<endl;
}

如上面所展示,可以通过一般性的容器操作接口(operator=,size()和operator[])直接操作容器。由于array也支持begin(),end()以获得迭代器,所以可以任何“调用begin()和end()”的操作

输出结果如下

11 22 33 44 0 0 0 0 0 0
11 22 33 44 0 0 0 0 42 9999999
sum: 10000151
-11 -22 -33 -44 0 0 0 0 -42 -9999999

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Reol520

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值