• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            boost源碼剖析之:boost::multi_array

            Posted on 2008-06-04 15:21 RichardHe 閱讀(561) 評論(0)  編輯 收藏 引用 所屬分類: [轉(zhuǎn)]
            本文只是將multi_array最基本的功能代碼做了一個扼要的分 析,正如文章開始所說,multi_array還有許多很有用的特性,如果讀者想充分了解multi_array的運(yùn)作機(jī)制與實(shí)現(xiàn)技巧,就請深入完整地分 析multi_array的代碼吧,相信一定會大有收獲的!

            動機(jī)

                  C++是一門自由的語言,允許你自由的表達(dá)自己的意圖,對不對? 所以我們既然可以new一個一維數(shù)組,也應(yīng)該可以new出多維數(shù)組,對不對?先來看一個例子:

                     int* pOneDimArr = new int[10]; //新建一個10個元素的一維數(shù)組

                     pOneDimArr[0] = 0; //訪問

                     int** pTwoDimArr = new int[10][20]; //錯誤!

                     pTwoDimArr[0][0] = 0; //訪問

                 但是,很可惜,三四兩行代碼的行為并非如你所想象的那樣——雖然從語法上它們看起來是那么自然

                 這里的問題在于,new int[10][20]返回的并非int**類型的指針,而是int (*)[20]類型的指針(這種指針被稱為行指針,對它“+1”相當(dāng)于在數(shù)值上加上一行的大小(本例為20),也就是說,讓它指向下一行),所以我們的代碼應(yīng)該像這樣:

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

            pTwoDimArr[1][2] = 0; //訪問

                 注意pTwoDimArr的類型——int(*)[20]是個很特殊的類型,它不能轉(zhuǎn)化為int**,雖然兩者索引元素的語法形式一樣,都是“p[i][j]”的形式,但是訪問內(nèi)存的次數(shù)卻不一樣,語義也不一樣。

                 最關(guān)鍵的問題還是:以上面這種樸素的方式來創(chuàng)建多維數(shù)組,有一個最大的限制,就是:除了第一維,其它維的大小都必須是編譯期確定的。例如:

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

                 這里N2,N3,N4必須都是編譯期常量,只有n1可以是變量,這個限制與多維數(shù)組的索引方式有關(guān)——無論多少維的數(shù)組都是線性存儲在內(nèi)存中的,所以:

                     pTwoDimArr[i][j] = 0;

            被編譯器生成的代碼類似于:

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

                 20就是二維數(shù)組的行寬,問題在于,如果允許二維數(shù)組的行寬也是動態(tài)的,這里編譯器就無法生成代碼(20所在的地方應(yīng)該放什么呢?)。基于這個原因,C++只允許多維數(shù)組的第一維是動態(tài)的。

                 不幸的是,正由于這個限制,C++中的多維數(shù)組就在大多數(shù)情況下變成了有名無實(shí)的無用之物。我們經(jīng)常可以在論壇上看到關(guān)于多維數(shù)組的問題,一般這類問題的核心都在于:如何模仿一個完全動態(tài)的多維數(shù)組。這里完全動態(tài)的意思是,所有維的大小都可以是動態(tài)的變量,而不僅是第一維。論壇上給出的答案不一而足,有的已經(jīng)相當(dāng)不錯,但是要么缺乏可擴(kuò)展性(即擴(kuò)展到N維的情況),要么在訪問元素的形式上遠(yuǎn)遠(yuǎn)脫離了內(nèi)建的多維數(shù)組的訪問形式,要么消耗了額外的空間。歸根到底,我們需要的是一個類似這樣的多維數(shù)組實(shí)現(xiàn):

             

                 //創(chuàng)建一個int型的3維數(shù)組,dim_sizes表示各維的大小:n1*n2*n3

                 multi_array<int,3> 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,能夠自動管理內(nèi)存,擁有和內(nèi)建多維數(shù)組一致的界面,并且各維的大小都可以是變量——正符合我們的要求。看起來,實(shí)現(xiàn)這個multi_array并非難事,但事實(shí)總是出乎想象,下面就是對boost中已有的一個multi_array實(shí)現(xiàn)的剖析——你幾乎肯定會發(fā)現(xiàn)一些出乎意料的(甚至是令人驚奇的)地方。

             

            Boost中的多維數(shù)組實(shí)現(xiàn)——boost::multi_array

             

            Boost庫中就有一個用于描述多維數(shù)組的功能強(qiáng)大的MultiArray庫。它實(shí)現(xiàn)了一個通用、與標(biāo)準(zhǔn)庫的容器一致的接口,并且具有與C++中內(nèi)建的多維數(shù)組一樣的界面和行為。正是這種設(shè)計(jì),使得MultiArray庫與標(biāo)準(zhǔn)庫組件甚至用戶自定義的泛型組件之間可以具有很好的兼容性,使它們能夠很好協(xié)同工作。除此之外,MultiArray還提供了諸如改變大小、重塑(reshaping)以及對多維數(shù)組的視圖訪問等極為有用的特性,從而使MultiArray比其它描述多維數(shù)組的組件(譬如:std::vector< std::vector<…> > )更為便捷、高效。對示例程序進(jìn)行調(diào)試、跟蹤是分析庫源代碼最有效的手段之一。我們就從MultiArray文檔中的示例程序入手:

             

            // 略去頭文件包含

            int main () {

                 // 創(chuàng)建一個尺寸為3×4×2的三維數(shù)組

                 #define DIMS 3 //數(shù)組是幾維的

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

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

                 // 為數(shù)組中元素賦值

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

                 ... ...

                 return 0;

            }

             

            在上述代碼中,(1-1)處的typedef是我們程序中使用的三維數(shù)組類型的聲明,很明顯,boost::multi_array的兩個模板參數(shù)分別代表數(shù)組元素的類型和數(shù)組的維度。而(1-2)處就是三維數(shù)組對象的構(gòu)造語句。boost::extents[3][4][2]的意思是:定義一個3*4*2的三維數(shù)組。

            下面我就為你層層剝開boost::extents的所有奧秘——

             

            extents——與內(nèi)建數(shù)組一致的方式

            boost::extents是一個全局對象,在base.hpp中:

             

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

                 ... ...

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

             

            可見extents的類型為extent_gen,這個extend_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

                   {

            std::copy(rhs.ranges_.begin(),rhs.ranges_.end(),ranges_.begin());

                          *ranges_.rbegin() = a_range;

                   }

                   extent_gen<NumRanges+1>

                          operator[](index idx)  // 2-3

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

                   };

             

             

            所以,boost::extents[3][4][2]展開為操作符調(diào)用的方式就相當(dāng)于:

            extents.operator [](3).operator [](4).operator [](2);

            extentsextent_gen<0>類型的,extents.operator [](3)應(yīng)調(diào)用函數(shù)2-3此時NumRange0,而返回類型是extent_gen<1>;再以該返回對象調(diào)用operator [](4)此時NumRange1,而返回類型則是extent_gen<2>了。再看函數(shù)2-3的內(nèi)容,其實(shí)就是將參數(shù)idxrange包裝一下再轉(zhuǎn)發(fā)給構(gòu)造函數(shù)(2-2),注意此時調(diào)用的是extent_gen<NumRange+1>類型的構(gòu)造函數(shù)。至于range(0,idx)則表示一個[0,idx)的區(qū)間。進(jìn)入構(gòu)造函數(shù)2-2,我們注意到extent_gen<...>中具有public的成員ranges_,聲明位于2-1處,而ranges_就是一個容器,保存了一系列的range

            跟蹤這些代碼,基本了解了extents的工作方式:每調(diào)用一次operator [],都會返回一個extent_gen<NumRange+1>類型的對象,所以,對于boost::extents[3][4][2],依次返回的是:

            extent_gen<1> => extent_gen<2> => extent_gen<3>

            最后一個也是最終的返回類型——extent_gen<3>。其成員ranges_中,共有[03)、[04)、[02)三組區(qū)間。這三組區(qū)間指定了我們定義的multi_array對象的三個維度的下標(biāo)區(qū)間,值得注意的是這些區(qū)間都是前閉后開的,即不包含上界值,這一點(diǎn)在后面的代碼中能夠看到。當(dāng)boost::extents準(zhǔn)備完畢后,就被傳入multi_array的構(gòu)造函數(shù),用于指定各維的下標(biāo)區(qū)間:

             

                 // multi_array.hpp

                 explicit multi_array(const extent_gen<NumDims>& ranges) :

                 super_type((T*)initial_base_,ranges) {

                     allocate_space();  // 2-5

                 }

             

            這里,multi_array接受了ranges參數(shù)中的信息,取出其中各維的下標(biāo)區(qū)間,然后保存,最后調(diào)用allocate_space()來分配底層內(nèi)存。

             

            使用extent_gen的好處

            使用boost::extents作參數(shù)的構(gòu)造過程和內(nèi)建多維數(shù)組的方式一致,簡練直觀,語義清晰。首先,boost::extents使用“[]”,能讓人很容易想到內(nèi)建多維數(shù)組的聲明,也很清晰地表達(dá)了每個方括號中數(shù)值的含義——表明各維度的容量區(qū)間;最關(guān)鍵的還是,使用boost::extents,可以防止用戶寫出錯誤的代碼,例如:

            multi_array<int,3> A(boost::extents[3][4][2][5]);//錯!多了一維!

            上面的語句是無法通過編譯,因?yàn)?/span>mult_array是個三維數(shù)組,而boost::extents后面卻跟了四個“[]”,這顯然是個錯誤;在語法層面,由于multi_array<int,3>的構(gòu)造函數(shù)只能接受extent_gen<3>類型的參數(shù),而根據(jù)我們前面對extents的分析,boost::extents[3][4][2][5]返回的卻是extent_gen<4>類型的對象,于是就會產(chǎn)生編譯錯誤。這種編譯期的強(qiáng)制措施阻止了用戶一不小心犯下的錯誤(如果你正在打瞌睡呢?),也很清晰明了地表達(dá)(強(qiáng)制)了語義的需求。

             

            另一種替代方案及其缺點(diǎn)

            另外,還有一種聲明各維大小的替代方式,就是使用所謂的Collection Concept,例如:

             

            // 聲明一個shape形狀),即各個維度的size

                 boost::array<int,3> shape = {{ 3, 4, 2 }};

                 array_type B(shape); //3*4*2的三維數(shù)組

             

            這種方式將調(diào)用multi_array的第二種構(gòu)造函數(shù):

             

                 // multi_array.hpp

                 template <class ExtentList>

                 explicit multi_array( ExtentList const& extents ) :

                 super_type((T*)initial_base_,extents) {

                     boost::function_requires< // 2-4

                 detail::multi_array::CollectionConcept<ExtentList> >();

                     allocate_space();  // 2-6

                 }

             

                這個構(gòu)造函數(shù)的形參extents只要是符合collection concept就可以了——shape的類型為boost::array,當(dāng)然符合這個concept。這個構(gòu)造函數(shù)的行為與接受extents_gen的構(gòu)造函數(shù)是一樣的——仍然是先取出各維的range保存下來,然后分配底層內(nèi)存。至于(2-4)處的代碼,功能就是在編譯期檢查模板參數(shù)ExtentList是否符合Collection concept,實(shí)現(xiàn)細(xì)節(jié)在此不再贅述。

            把這種方式與使用extent_gen的方式作一個簡單的比較,很容易就看出優(yōu)劣:采用這種方式,就不能保證編譯期能夠進(jìn)行正確性的檢查了,例如:

             

            boost::array<int,4> shape = {{3,4,2,5}}; //一個四維數(shù)組的shape

            multi_array<int,3> A(shape); // 竟然可以通過編譯!!

             

            這里,用一個四維的shape來指定一個三維multi_array顯然是錯誤的,但是居然通過了編譯,這是由于這個構(gòu)造函數(shù)將它的參數(shù)extents作為一個普通的collection來對待,構(gòu)造函數(shù)根據(jù)自己的需求用iteratorextents中取出它所需要的數(shù)值——A是三維數(shù)組,于是構(gòu)造函數(shù)從shape中取出前三個數(shù)值作為A三個維度的下標(biāo)區(qū)間,而不管shape究竟是包含了幾個數(shù)值。這樣的語句在語義上是不清晰甚至錯誤的。但是既然這樣的構(gòu)造函數(shù)存在,設(shè)計(jì)者自然有他的道理,文檔中就明確的表明,這個構(gòu)造函數(shù)最大的用處就是編寫維度無關(guān)(dimension-independent)的代碼,除此之外multi_array庫默認(rèn)為前一種構(gòu)造函數(shù)。

             

            multi_array的架構(gòu)

            無論采用哪一種構(gòu)造函數(shù),代碼流程卻是相似的——將一系列下標(biāo)區(qū)間傳入基類的構(gòu)造函數(shù)中去,基類構(gòu)造完成之后就調(diào)用相同的allocate_space()函數(shù)(見(2-5)和(2-6)處),allocate_space,顧名思義,應(yīng)該是為多維數(shù)組的元素分配空間的。但是對于這樣在派生類而非基類的構(gòu)造中分配存儲空間的設(shè)計(jì),可能的合理解釋就是:基類是個適配器(adapter),它決定了一切對原始數(shù)據(jù)的訪問規(guī)則,描述了multi_array對外界的接口。

            順著基類的構(gòu)造函數(shù),我們繼續(xù)向multi_array的深處探索。

                

            // multi_array_ref.hpp

            template <typename T, std::size_t NumDims>

            class multi_array_ref :  //multi_array的基類!!

            public const_multi_array_ref<T,NumDims,T*>

            {

                 typedef const_multi_array_ref<T,NumDims,T*> super_type;

                 ... ...

                 explicit multi_array_ref(T* base, //指向數(shù)組存儲空間的指針

                     const extent_gen<NumDims>& ranges): //下標(biāo)區(qū)間

                 super_type(base,ranges)   //把初始化的任務(wù)轉(zhuǎn)發(fā)給基類3-1

                 { }

                     ... ...

            };

             

            // multi_array_ref.hpp

            class const_multi_array_ref : //multi_array_ref的基類!管理底層存儲!

            public multi_array_impl_base<T,NumDims>

            {

                 ... ...

                 explicit const_multi_array_ref(TPtr base,

                     const extent_gen<NumDims>& ranges) :

                     base_(base), storage_(c_storage_order())   // 3-2

                     { init_from_extent_gen(ranges); }

                 ... ...

                 storage_order_type storage_;//支持多種存儲策略!(3-3

                

            };

             

            multi_array基類對象的構(gòu)造之路途徑(3-1)處multi_array_ref的構(gòu)造函數(shù),延伸至(3-2)處const_multi_array_ref的構(gòu)造函數(shù)——這里看似一個終結(jié),因?yàn)樵贈]有參數(shù)傳遞給 const_multi_array_ref的基類multi_array_impl_base了。但是心中還是疑惑:為什么會有如此多層的繼承結(jié)構(gòu)?這樣的類層次結(jié)構(gòu)設(shè)計(jì)究竟有什么玄機(jī)呢?

             

            多層繼承的奧秘——復(fù)用性

            轉(zhuǎn)到基類const_multi_array_ref的聲明,似乎可以看出一些端倪:

             

            template< ... >

            class const_multi_array_ref {

                 ... ...

                 //和所有的STL容器一致的迭代器界面!!

                 const_iterator     begin() const;

                 const_iterator     end() const;

                 ... ...

                 //std::vector一致的元素訪問界面!!

                 const_reference operator[](index i) const;

                 ... ...

            };

             

            看到上面這些聲明,是不是有些面熟?STL!對,這些成員函數(shù)的聲明是與STLcontainer concept完全一致的。所謂與STL的兼容性正是在這里體現(xiàn)出來了。而const_multi_array_ref更是類如其名const_multi_array_ref中所有訪問元素、查詢數(shù)組信息等成員函數(shù)都返回constreferenceiterator。而反觀multi_array_ref的聲明,其中只比const_multi_array_ref多了訪問元素、查詢數(shù)組信息的對應(yīng)的non-const版本成員函數(shù)。那么const_multi_array_ref的基類multi_array_impl_base的職責(zé)是什么呢?接著展開類multi_array_impl_base的聲明,

            multi_array_impl_base是屬于實(shí)現(xiàn)細(xì)節(jié)的,它的作用只是根據(jù)數(shù)組信息(const_multi_array_ref中的成員變量)計(jì)算偏移量、步長等,也就是把多維的下標(biāo)最終轉(zhuǎn)化為一維偏移量。而multi_array_impl_base的基類——或者是value_accessor_n或者是value_accessor_one——的功能就是提供一個對原始數(shù)據(jù)的訪問。這在下文詳述。

            至此,對multi_array的基類子對象大致有了了解——它們的繼承關(guān)系如下:

                multi_array -> multi_array_ref -> const_multi_array_ref -> multi_array_impl_base -> value_accessor_n/value_accessor_one

            其中每一層都擔(dān)任各自的角色:

            ¨        multi_array : 為數(shù)組元素分配空間,將各種操作轉(zhuǎn)發(fā)至基類

            ¨        multi_array_ref : 提供與STL容器一致的數(shù)據(jù)訪問界面。也可以獨(dú)立出來作為一個adapter使用。

            ¨        const_multi_array_ref : 提供constSTL數(shù)據(jù)訪問界面。也可以作為一個const adapter使用。

            ¨        multi_array_impl_base及其基類 :最底層實(shí)現(xiàn),提供一組對原始數(shù)據(jù)的基本操作。

            這種架構(gòu)看似復(fù)雜,卻提供了極高的復(fù)用性,其中的(const_)multi_array_ref都可以獨(dú)立出來作為一個adapter使用——例如:

             

                int a[24]; //一維的10個元素?cái)?shù)組

                //把一維數(shù)組a看成一個3*4*2的三維數(shù)組:

                multi_array_ref<int,3> arr_ref(a,boost::extents[3][4][2]);

                arr_ref[i][j][k] = value; //multi_array一樣的使用界面

             

            倘若你不想讓multi_array來自動分配內(nèi)存的話,你可以自行分配數(shù)組(可以位于棧上或堆上)然后用multi_array_ref把它包裝成一個多維的數(shù)組。

             

            multi_array的存儲策略

            接下來就來看看multi_array的存儲策略,例如C風(fēng)格的多維數(shù)組存儲方式是按行存儲fortran恰恰相反,是按列存儲,甚至,用戶可能有自己的存儲策略要求。那么,如何支持多種風(fēng)格的存儲策略呢?秘密就在于代碼3-3const_multi_array_ref的成員storage_——其類型為storage_order_type下面的聲明指出了storage_order_type本來面目”——general_storage_order<NumDims>

                

            // multi_array_ref.hpp

                 ... ...

                 typedef general_storage_order<NumDims> storage_order_type;

                 ... ...

                 // storage_order.hpp

                 template <std::size_t NumDims>

                 class general_storage_order {

                 general_storage_order(const c_storage_order&){ //4-1

                     for (size_type i=0; i != NumDims; ++i)

                     { ordering_[i] = NumDims - 1 - i; }

                     ascending_.assign(true);

                     }

                 ... ...

                 boost::array<size_type,NumDims> ordering_;

                 boost::array<bool,NumDims> ascending_;

                 };

             

            4-1處的構(gòu)造函數(shù)中ordering_ascending_是兩個數(shù)組當(dāng)函數(shù)4-1執(zhí)行完畢后ordering_中的元素應(yīng)當(dāng)是{NumDims-1, NumDims-2,...1,0}如果將這些元素作為各維度存儲順序的標(biāo)識——具有較小ordering_值的維度先存儲——那么這和C語言中的存儲方式就完全一致了ascending_勿庸置疑就是用來表明各維度是否升序存儲。其實(shí)general_storage_order還有一個模板構(gòu)造函數(shù),它是為了支持更為一般化的存儲策略(例如fortran的按列存儲或用戶自定義的存儲策略)。這里不作詳述。

            除了存儲策略const_multi_array_ref的構(gòu)造還通過調(diào)用init_from_extent_gen函數(shù)extents中的內(nèi)容取出來進(jìn)行處理并以此設(shè)定其它若干表述多維數(shù)組的變量((3-3處其它一些變量),具體細(xì)節(jié)不再贅述。

            現(xiàn)在關(guān)于一個多維數(shù)組的所有信息都已經(jīng)準(zhǔn)備齊備,可謂萬事具備,只欠空間’”multi_array下面要做的就是調(diào)用前面提到的allocate_space來為數(shù)組中的元素分配空間了。

             

            // multi_array.hpp

            void allocate_space() {

                 ... ...

                 base_ = allocator_.allocate(this->num_elements(),no_hint);

                 ... ...  std::uninitialized_fill_n(base_,allocated_elements_,T());

            }

             

            原來在底層,存儲仍然是退化為一維數(shù)組的存儲std::uninitialized_fill_n負(fù)責(zé)把該數(shù)組進(jìn)行缺省初始化allocate_space使用allocator_分配一塊連續(xù)空間用以存儲元素其中num_elements()返回的就是數(shù)組各維度的大小的乘積數(shù)組的總元素個數(shù)。分配完之后,就將首指針賦給表述數(shù)組基地址的成員base_。至此multi_array的構(gòu)造工作終于大功告成了。

             

            一致性界面——GP的靈魂

            multi_array的另一重要特性就是以支持與內(nèi)建多維數(shù)組相同的訪問方式,即是說,multi_array支持以連續(xù)的operator[]來訪問數(shù)組元素。就以1-3處的賦值語句為例,讓我們看看multi_array是如何支持這種與內(nèi)建數(shù)組兼容的訪問方式的。

             

            // multi_array_ref.hpp

            // 使用operator[]來訪問元素

            reference operator[](index idx) {

                 return super_type::access(boost::type<reference>(),

                               idx,origin(),this->shape(),this->strides(),

                               this->index_bases());

            }

             

            這個調(diào)用轉(zhuǎn)入了value_accessor_n::access(...)之中

             

             

            // base.hpp

            // in class value_accessor_n

            template <typename Reference, typename TPtr>

            Reference access(boost::type<Reference>,

                               index idx,TPtr base,const size_type* extents,

                               const index* strides,const index* index_base)

            {

                   TPtr newbase = base + idx * strides[0];

                   return Reference(newbase,extents+1,

                                   strides+1,index_base+1);

            }

             

                這個連續(xù)調(diào)用operator[]的過程和extend_gen是很類似的——每調(diào)用一層就返回一個“proxy”,然后在其上繼續(xù)調(diào)用operator[],如此往復(fù)...

            那么如果以A[x1][x2][x3]方式訪問A中的元素,就相當(dāng)于

            A.operator[x1].operator[x2].operator[x3] //連續(xù)調(diào)用“[]”

            這三次operator[]調(diào)用返回的類型依次為

            sub_array<T,2> -> sub_array<T,1> -> T&

                注意,最后一次調(diào)用“[]”返回的恰好是對元素的引用(這就剛好證明了前面說的,只有“[]”的個數(shù)和數(shù)組的維數(shù)相同的時候,才能夠取出元素,否則你得到的要么sub_array<...>,要么會由于試圖在T&上繼續(xù)調(diào)用“[]”而編譯失敗)那么,這一切究竟是如何做到的呢?

             

            sub_array的秘密

            sub_array的定義sub_array.hpp

             

            // sub_array.hpp

            template <typename T, std::size_t NumDims>

            class sub_array : public const_sub_array<T,NumDims,T*>;

             

            template <typename T, std::size_t NumDims, typename TPtr>

            class const_sub_array :

                     public multi_array_impl_base<T,NumDims>;

            //base.hpp

            template <typename T, std::size_t NumDims>

            class multi_array_impl_base:public

             value_accessor_generator<T,mpl::size_t<NumDims> >::type ;

             

            唔,原來sub_array最終繼承自multi_array_impl_base,后者的基類是value_accessor_generator中的一個typedef,會根據(jù)NumDims的不同而成為不同的類型:

             

            // base.hpp

            template <typename T, typename NumDims>

            struct value_accessor_generator {

                 ... ...

                 typedef typename  //如果NumDims1,則類型為value_accessor_one

                 mpl::apply_if_c<(dimensionality == 1),

                       choose_value_accessor_one<T>,

                       choose_value_accessor_n<T,dimensionality>

                  >::type type; //把這個類型作為multi_array_impl_base的基類!

            };

             

            很顯然,如果dimensionality == 1,那么::type就是value_accessor_one<T>。而只有對value_accessor_one使用“[]”才能返回T&,否則,::type被推導(dǎo)為:value_accessor_n,這只是個“proxy”而已,對它運(yùn)用“[]”只會返回sub_array<T,NumDims-1>,從而繼續(xù)這個連續(xù)調(diào)用“[]”的過程

             

            取出元素

                根據(jù)上面的分析,取元素的任務(wù)最終交給value_accessor_one,其成員函數(shù)access如下:

                 template <typename Reference, typename TPtr>

                 Reference access(boost::type<Reference>,index idx,TPtr base,

                               const size_type*,const index* strides,

                               const index*) const {

                     return *(base + idx * strides[0]); //終于取出了數(shù)據(jù)!

                 }

             

            這里,access的返回類型Reference就是T&,即數(shù)組中元素類型的引用,從而可以將指定元素的引用返回,達(dá)到訪問數(shù)組元素的目的。看到這里,multi_array以內(nèi)建數(shù)組訪問方式訪問數(shù)組元素的過程基本已經(jīng)弄清楚了,至于其中一些細(xì)節(jié),尤其是計(jì)算地址的細(xì)節(jié),譬如:偏移量的計(jì)算、步長的使用等,皆已略去了。

            現(xiàn)在也許你會有這樣的疑惑:以內(nèi)建數(shù)組訪問方式訪問數(shù)組元素的能力真的如此重要嗎?費(fèi)這么大力氣、寫這么多代碼還不如以多參數(shù)的方式重載operator[]呢!這么大代價真的值得嗎?值得!這是勿庸置疑的。以內(nèi)建數(shù)組訪問方式訪問數(shù)組元素的能力最重要的表現(xiàn)就是,可以使使用者以與內(nèi)建數(shù)組一致的方式對待multi_array。舉個例子:用戶編寫了一個函數(shù)模板,

             

            template <typename ReturnType, typename _3DArray>

            ReturnType func(_3Array& mda){//可以作用于內(nèi)建多維數(shù)組

            ... ...

                 mda[x][y][z] = mda[z][x][y];

               ... ...

            }

             

            因?yàn)橛辛艘詢?nèi)建數(shù)組訪問方式訪問數(shù)組元素的能力,這個func模板可以同時應(yīng)用在內(nèi)建數(shù)組和multi_array(否則用戶就得為multi_array提供一個單獨(dú)的重載版本),如此一來,代碼的可重用性、可擴(kuò)展性都大大提高了。

             

            效率

            效率是C++永恒的主題,MultiArray庫也不例外。執(zhí)行時間效率上,縱觀MultiArray庫對數(shù)組元素的訪問代碼,雖然函數(shù)調(diào)用嵌套層數(shù)甚多,但多數(shù)調(diào)用都是簡單的轉(zhuǎn)發(fā),在現(xiàn)在高度成熟的C++編譯器下,這些轉(zhuǎn)發(fā)的函數(shù)調(diào)用代碼應(yīng)該可以很輕易地被優(yōu)化掉,所以在效率上幾乎沒有什么損失。在空間效率方面,由于大量運(yùn)用模板技術(shù),基本能夠在編譯期決定的內(nèi)容都已決定,沒有為運(yùn)行期帶來不必要的空間上的負(fù)擔(dān)。總的看來,Boost.MultiArray庫的確是難得的高效又通用的多維數(shù)組的實(shí)現(xiàn)。

             

            結(jié)語

            本文只是將multi_array最基本的功能代碼做了一個扼要的分析,正如文章開始所說,multi_array還有許多很有用的特性,如果讀者想充分了解multi_array的運(yùn)作機(jī)制與實(shí)現(xiàn)技巧,就請深入完整地分析multi_array的代碼吧,相信一定會大有收獲的!

            目錄(展開《boost源碼剖析》系列文章)

            本文來自:http://blog.csdn.net/pongba/archive/2007/04/11/1560738.aspx

            posts - 94, comments - 138, trackbacks - 0, articles - 94

            Copyright © RichardHe

            久久国语露脸国产精品电影| 伊人热热久久原色播放www| 国产精品九九久久免费视频 | 久久美女人爽女人爽| 久久久久一级精品亚洲国产成人综合AV区| 性高朝久久久久久久久久| 国产精品久久久天天影视| 亚洲国产精品无码久久青草| 久久天堂电影网| 人妻精品久久久久中文字幕一冢本| 狠狠色综合久久久久尤物| 久久精品中文騷妇女内射| 欧美激情精品久久久久久久| 国产精品久久毛片完整版| 777午夜精品久久av蜜臀| 久久www免费人成看国产片| 精品久久久久久综合日本| 99精品国产综合久久久久五月天| 久久精品国产一区二区三区不卡| 国产成人精品久久一区二区三区 | 亚洲国产成人久久综合区| 久久国产成人午夜aⅴ影院| 久久国产精品久久久| 久久精品中文无码资源站| 亚洲国产精品无码久久久不卡| 伊人久久大香线蕉综合网站| 欧美午夜A∨大片久久 | 亚洲欧美成人久久综合中文网 | 久久久久久精品久久久久| 亚洲乱码日产精品a级毛片久久| 久久精品夜色噜噜亚洲A∨| 办公室久久精品| 久久精品国产精品亚洲下载| 国产精品日韩深夜福利久久 | 久久一日本道色综合久久| 蜜臀av性久久久久蜜臀aⅴ麻豆 | 久久久久久久久久久久中文字幕| 亚洲AV无码久久| 久久亚洲AV成人无码电影| 久久久久99精品成人片直播| 热re99久久精品国99热|