Boost源码笔记:boost::multi_array

本文深入分析了Boost库中的multi_array实现,探讨了其为何能够模仿C++内建多维数组的访问方式。通过多层继承结构,包括multi_array、multi_array_ref和const_multi_array_ref,实现了一致的访问接口,提供了与STL容器兼容的数据访问。此外,文章介绍了如何通过extent_gen动态指定多维数组尺寸,并利用storage_order支持不同的存储策略。通过对multi_array构造过程和元素访问方式的解析,展示了其高效率和灵活性。
摘要由CSDN通过智能技术生成

Boost源码笔记:boost::multi_array

                                                           谢轩 /

动机

         C++是一门自由的语言,允许你自由的表达自己的意图,对不对? 所以我们既然可以new一个一维数组,也应该可以new出多维数组,对不对?先来看一个例子:

        int* pOneDimArr = new int[10]; //新建一个10个元素的一维数组

        pOneDimArr[0] = 0; //访问

        int** pTwoDimArr = new int[10][20]; //错误!

        pTwoDimArr[0][0] = 0; //访问

    但是,很可惜,三四两行代码的行为并非如你所想象的那样——虽然从语法上它们看起来是那么自然

    这里的问题在于,new int[10][20]返回的并非int**类型的指针,而是int (*)[20]类型的指针(这种指针被称为行指针,对它“+1”相当于在数值上加上一行的大小(本例为20),也就是说,让它指向下一行),所以我们的代码应该像这样:

int (*pTwoDimArr)[20] = new int[i][20]; //正确

pTwoDimArr[1][2] = 0; //访问

    注意pTwoDimArr的类型——int(*)[20]是个很特殊的类型,它不能转化为int**,虽然两者索引元素的语法形式一样,都是“p[i][j]”的形式,但是访问内存的次数却不一样,语义也不一样。

    最关键的问题还是:以上面这种朴素的方式来创建多维数组,有一个最大的限制,就是:除了第一维,其它维的大小都必须是编译期确定的。例如:

       int (*pNdimArr)[N2][N3][N4] = new int[n1][N2][N3][N4];

    这里N2,N3,N4必须都是编译期常量,只有n1可以是变量,这个限制与多维数组的索引方式有关——无论多少维的数组都是线性存储在内存中的,所以:

        pTwoDimArr[i][j] = 0;

被编译器生成的代码类似于:

        *( (int*)pTwoDimArr+i*20+j ) = 0;

    20就是二维数组的行宽,问题在于,如果允许二维数组的行宽也是动态的,这里编译器就无法生成代码(20所在的地方应该放什么呢?)。基于这个原因,C++只允许多维数组的第一维是动态的。

    不幸的是,正由于这个限制,C++中的多维数组就在大多数情况下变成了有名无实的无用之物。我们经常可以在论坛上看到关于多维数组的问题,一般这类问题的核心都在于:如何模仿一个完全动态的多维数组。这里完全动态的意思是,所有维的大小都可以是动态的变量,而不仅是第一维。论坛上给出的答案不一而足,有的已经相当不错,但是要么缺乏可扩展性(即扩展到N维的情况),要么在访问元素的形式上远远脱离了内建的多维数组的访问形式,要么消耗了过多额外的空间。归根到底,我们需要的是一个类似这样的多维数组实现:

 

//创建一个int型的DIMS维数组,dim_sizes用于指定各维的大小,即n1*n2*n3

multi_array<int,DIMS> ma ( dim_sizes[n1][n2][n3] );

ma[i][j][k] = value; //为第ijk列的元素赋值

ma[i][j] = value; //编译错!

ma[i] = value; //编译错!

ma[i][j][k][l] = value;//编译错!

 

这样一个multi_array,能够自动管理内存,拥有和内建多维数组一致的界面,并且各维的大小都可以是变量——正符合我们的要求。看起来,实现这个multi_array并非难事,但事实总是出乎意料,下面就是对boost中已有的一个multi_array实现的剖析——你几乎肯定会发现一些出乎意料的(甚至是令人惊奇的)地方。

 

Boost中的多维数组实现——boost::multi_array

Boost库中就有一个用于描述多维数组的功能强大的MultiArray库。它实现了一个通用、与标准库的容器一致的接口,并且具有与C++中内建的多维数组一样的界面和行为。正是基于这种通用性的设计,MultiArray库与标准库组件甚至用户自定义的泛型组件之间可以具有很好的兼容性,并能够很好的协同工作。

除此之外,MultiArray还提供了诸如改变大小、重塑(reshaping)以及对多维数组的视图访问等极为有用的特性,从而使MultiArray比其它描述多维数组的方式(譬如:std::vector< std::vector<...> > )更为便捷、高效。

下面我们就逐层深入,去揭开boost::multi_array的神秘面纱——对示例程序进行调试、跟踪是分析库源代码最有效的手段之一。我们就从MultiArray文档中的示例程序入手:

 

// 略去头文件包含

int main () {

    // 创建一个尺寸为3×4×2的三维数组

    #define DIMS 3 //数组是几维的

    typedef boost::multi_array<double,DIMS> array_type; // (1-1)

    array_type A(boost::extents[3][4][2]); // (1-2)

    // 为数组中元素赋值

    A[1][2][0] = 120;     // (1-3)

    ... ...

    return 0;

}

 

在上述代码中,boost::multi_array的两个模板参数分别代表数组元素的类型和数组的维度1-2处是三维数组对象的构造语句。boost::extents[3][4][2]则用于指明该数组各维的大小,这里的含义是:定义一个3*4*2的三维数组。你肯定对boost::extents心存疑惑——为什么对它可以连用“[]”?如果用多了一个或用少了一个“[]”又会如何?下面我就为你层层剥开boost::extents的所有奥秘——

 

extents——与内建数组一致的方式

boost::extents是一个全局对象,在base.hpp中:

 

    typedef detail::multi_array::extent_gen<0> extent_gen;

    ... ...

    multi_array_types::extent_gen extents; //注意它的类型!

 

可见extents的类型为extent_gen,后者位于extent_gen.hpp中:

 

// extent_gen.hpp

template <std::size_t NumRanges>

class extent_gen {

    range_list ranges_;    // 2-1

... ...

extent_gen(const extent_gen<NumRanges-1>& rhs, const range& a_range)

{ // 2-2

 ... //

}

extent_gen<NumRanges+1> operator[](index idx) //(2-3)

{ return extent_gen<NumRanges+1>(*this,range(0,idx)); }

    //返回一个extent_gen,不过第二个模板参数增加了1

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值