摘 要:動(dòng)態(tài)控件數(shù)組是應(yīng)用程序設(shè)計(jì)中經(jīng)常用到的重要手段之一。合理地使用動(dòng)態(tài)控件數(shù)組不但能簡(jiǎn)化程序、方便維護(hù),還可以節(jié)約內(nèi)存空間,對(duì)動(dòng)態(tài)控件數(shù)組的研究有助于增加應(yīng)用程序靈活性,能有效提高編程效率。鑒于此,提出了在C++Builder環(huán)境下基于容器對(duì)象的動(dòng)態(tài)控件數(shù)組實(shí)現(xiàn)方案,分別給出利用三種容器對(duì)象TList,DynamicArray和Vector實(shí)現(xiàn)動(dòng)態(tài)控件數(shù)組的思想,對(duì)三者的性能進(jìn)行比較分析,并總結(jié)了實(shí)現(xiàn)動(dòng)態(tài)控件數(shù)組的原理、方法和技巧。
關(guān)鍵詞:動(dòng)態(tài)控件數(shù)組; 容器; TList; DynamicArray; Vector
中圖分類號(hào):TN911.7-34; TP311.11
文獻(xiàn)標(biāo)識(shí)碼:A
文章編號(hào):1004-373X(2012)01-0146-04
Research on dynamic control arrays based on container object
LIU Gen, YANG Yu-qin, JIANG Tian-fa
(College of Computer Science, South-central University for Nationalities, Wuhan 430073, China)
Abstract:
Dynamic control arrays are often used in application programming. Reasonably using dynamic control arrays can simplify application procedures and their maintenance, save memory space, increase the flexibility of application procedure, and effectively improve the efficiency of application programming. The schemes of dynamic control arrays based on container object under C++Builder are presented. Three kinds of container objects TList, DynamicArray and Vector are used to realize dynamic control arrays, whose performance are analyzed and compared. The principle, methods and technique of dynamic control arrays are summarized.
Keywords: dynamic component arrays; container; TList; DynamicArray; Vector
收稿日期:2011-08-11
基金項(xiàng)目:國(guó)家自然科學(xué)基金項(xiàng)目(40571128);湖北省教育廳科研項(xiàng)目(B20110804)
0 引 言
動(dòng)態(tài)數(shù)組[1]可以在運(yùn)行期間改變數(shù)組大小,具有很強(qiáng)的靈活性,為開發(fā)者帶來極大便利,廣泛應(yīng)用于各個(gè)領(lǐng)域[2-3]。動(dòng)態(tài)控件數(shù)組就是一種特殊的動(dòng)態(tài)數(shù)組,它的元素類型是控件類型??丶?shù)組應(yīng)該包含以下三個(gè)要素:
(1) 允許多個(gè)控件共享同一個(gè)事件句柄。即動(dòng)態(tài)生成的多個(gè)同類型控件可以使用相同的事件名。這一點(diǎn)是BCB已經(jīng)具備的功能。
(2) 可以在運(yùn)行期間添加或者刪除控件。BCB已經(jīng)提供了這種機(jī)制。
(3) 可以方便地將多個(gè)控件組合為控件數(shù)組進(jìn)行管理和使用。要組合控件最好的方法就是使用容器。BCB中能夠?qū)崿F(xiàn)控件數(shù)組的容器可以分為三種:第一種是VCL(Visual Componet Library,可視化組件庫(kù))中的TList類;第二種是VCL中的DynamicArray類;第三種是STL[4](Standard Template Library,標(biāo)準(zhǔn)模板庫(kù))中的Vector容器類。
文獻(xiàn)[5]提出一種基于TList類的控件數(shù)組實(shí)現(xiàn)方法,在其基礎(chǔ)上本文提出基于容器類的動(dòng)態(tài)控件數(shù)組實(shí)現(xiàn)方案,并詳細(xì)分析了其性能效率。
1 基于TList類的控件數(shù)組
1.1 VCL中的TList類
TList是VCL提供的一個(gè)用于維護(hù)對(duì)象列表的類,它是以鏈?zhǔn)酱鎯?chǔ)指針的方式實(shí)現(xiàn)的。它存儲(chǔ)了用于維護(hù)各種類指針的索引,這些索引存有指向各種對(duì)象的指針。為方便在對(duì)象列表中添加、刪除、查找、訪問、排序或者重組對(duì)象,TList提供了方便全面的鏈表功能,包括Capacity,Count,Items等主要屬性和Add,Insert,Delete,Clear,F(xiàn)irst,Last等主要方法,各屬性和方法詳細(xì)信息如表1所示。
1.2 基于TList類的控件數(shù)組的實(shí)現(xiàn)思想
文獻(xiàn)[5]提出一種基于TList類的控件數(shù)組實(shí)現(xiàn)方案,下面簡(jiǎn)要闡述其基本思想。
定義TList類型指針對(duì)象,建立控件數(shù)組CList,并調(diào)用構(gòu)造函數(shù)對(duì)其進(jìn)行初始化:
TList *CList = new TList() ;
動(dòng)態(tài)生成控件對(duì)象Button,添加到CList中:
TButton *Button=new TButton(this) ;
CList->Add(Button) ;
表1 TList類的主要屬性和方法
名稱相關(guān)信息
Capacity標(biāo)識(shí)TList對(duì)象維護(hù)的指針數(shù)組大小
Count標(biāo)識(shí)Items數(shù)組中項(xiàng)的數(shù)量
Items可以獲得數(shù)組中指定對(duì)象的指針
Add在列表的末尾插入新項(xiàng)
Clear刪除列表中所有項(xiàng)
Delete刪除列表中指定位置的項(xiàng)
First返回Items數(shù)組中第一個(gè)指針
Insert向Items數(shù)組中指定位置插入新的項(xiàng)
Last返回Items數(shù)組中最后一個(gè)指針
為Button聲明并自定義事件MBClick,讓所有動(dòng)態(tài)生成的控件共享事件句柄:
void __fastcall TForm1::MBClick(
TObject *Sender) ;
Button->OnClick=MBClick ;
通過TList對(duì)象的Items屬性訪問CList中的對(duì)象,甚至可以在自定義事件中訪問TList對(duì)象中動(dòng)態(tài)生成的控件:
void __fastcall TForm1::MBClick(TObject
*Sender)
{
ShowMessage(((TButton*)(CList->Items[CList->Count-1]))->Caption;
}
刪除CList中指定成員對(duì)象(第i個(gè))時(shí),先將CList中該成員對(duì)象的類指針?biāo)饕b入臨時(shí)指針,然后刪除臨時(shí)指針?biāo)笇?duì)象,同時(shí)刪除控件數(shù)組中的指針記錄:
TButton *temp = (TButton *)CList->Items[i] ;
delete temp ;
CList->Delete(i) ;
清空整個(gè)CList時(shí),先刪除其中所有成員對(duì)象,再用Clear方法清空。程序結(jié)束時(shí)CList需要銷毀,可在窗體的析構(gòu)函數(shù)中加入以下代碼:
delete CList;
2 基于DynamicArray模板的控件數(shù)組
2.1 VCL中的DynamicArray模板類
DynamicArray是VCL提供的一個(gè)用于實(shí)現(xiàn)變長(zhǎng)動(dòng)態(tài)數(shù)組的模板類[6],使用二進(jìn)制數(shù)據(jù)塊兼容Object Pascal動(dòng)態(tài)數(shù)組實(shí)現(xiàn)。DynamicArray也可以看作是一個(gè)可以存放各種控件對(duì)象的容器,只需將動(dòng)態(tài)數(shù)組聲明中的數(shù)據(jù)類型設(shè)置為相應(yīng)控件類型即可。為方便管理動(dòng)態(tài)數(shù)組,DynamicArray模板提供了Length,High,Low等屬性和Copy,CopyRange等方法,并重載了賦值運(yùn)算符“=”、比較運(yùn)算符“==”和訪問數(shù)組元素的“[]”運(yùn)算符,如表2所示。
表2 DynamicArray類的主要屬性、方法和操作符
名稱相關(guān)信息
屬性
Length動(dòng)態(tài)數(shù)組長(zhǎng)度
High動(dòng)態(tài)數(shù)組的下標(biāo)上界Length-1
Low動(dòng)態(tài)數(shù)組的下標(biāo)下界零
方法
Copy將數(shù)組內(nèi)容復(fù)制到指定數(shù)組中
CopyRange復(fù)制一段連續(xù)元素到指定數(shù)組中
操作符
“=”引用型賦值操作符
“==”判斷動(dòng)態(tài)數(shù)組指針是否相同
“[]”用于訪問動(dòng)態(tài)數(shù)組中的元素
2.2 基于DynamicArray模板的控件數(shù)組實(shí)現(xiàn)思想
利用DynamicArray模板實(shí)現(xiàn)動(dòng)態(tài)控件數(shù)組時(shí),需要將動(dòng)態(tài)數(shù)組的類型設(shè)置為某一控件類型,通過在程序中動(dòng)態(tài)修改動(dòng)態(tài)數(shù)組的Length屬性來實(shí)現(xiàn)數(shù)組大小的動(dòng)態(tài)變化。下面以動(dòng)態(tài)按鈕數(shù)組(BArray)為例,詳細(xì)說明動(dòng)態(tài)控件數(shù)組實(shí)現(xiàn)思想。
聲明動(dòng)態(tài)按鈕數(shù)組:
DynamicArray
動(dòng)態(tài)地向BArray中添加成員對(duì)象時(shí),先將BArray的Length屬性增加1,以便為待添加的新對(duì)象預(yù)留空間:
BArray.Length = BArray.Length + 1 ;
創(chuàng)建待添加的控件對(duì)象并裝入新開辟的空間:
BArray[BArray.High] = (new TButton(this)) ;
獲取新添加的控件對(duì)象:
TButton* Button = BArray[BArray.High] ;
刪除指定成員(比如第i個(gè))對(duì)象時(shí),先刪除動(dòng)態(tài)數(shù)組中第i個(gè)成員對(duì)象:
delete BArray[i] ;
然后使用循環(huán)語句將被刪除成員對(duì)象后面的元素全部前移:
for(int k = i ; k < BArray.High ; k++)
{
BArray[i] = BArray[i+1] ;
}
最后將BArray的Length屬性減少1:
BArray.Length = BArray.Length - 1 ;
如果要清空動(dòng)態(tài)數(shù)組BArray中的所有成員,先要使用循環(huán)語句逐一刪除BArray中的所有元素:
for(int k = 0 ; k { delete BArray[k] ; } 然后將動(dòng)態(tài)數(shù)組BArray的Length屬性設(shè)置為0,但無需使用delete語句銷毀BArray: BArray.Length = 0 ; 3 基于Vector容器類的控件數(shù)組 3.1 STL中的Vector容器類 Vector是STL容器類中最簡(jiǎn)單的容器類,通常也是效率最高的容器類。Vector[7]是一個(gè)隨機(jī)存取的Sequence容器,采用連續(xù)的內(nèi)存空間存放數(shù)據(jù)。它支持常數(shù)時(shí)間的尾部插入和刪除、線性時(shí)間的其他位置的插入和刪除。Vector模板類class vector接口成員(public)如表3所示。 表3 Vector接口類中包含的主要內(nèi)容 類型定義value_type,allocator_type,reference, iterator,size_type,difference_type; 構(gòu)造函數(shù)vector (size_type, const T, const Allocator = Allocator()); 拷貝構(gòu)造函數(shù)vector (const vector 析構(gòu)函數(shù)~vector (); 迭代器begin ();end (); 關(guān)于容量大小的函數(shù)size();max_size();resize(size_type, T);capacity () ;empty () ;reserve (size_type); 用于訪問的操作和函數(shù)operator[] (size_type);at (size_type);front();back(); 更改容器內(nèi)容的函數(shù)push_back (const T);pop_back ();void insert (iterator, size_type, const T);erase (iterator);swap (vector 3.2 基于Vector容器類的控件數(shù)組的實(shí)現(xiàn)思想 基于Vector容器實(shí)現(xiàn)動(dòng)態(tài)控件數(shù)組的思想[8],將Vector中元素類型設(shè)置為相應(yīng)的控件類型,然后利用Vector提供的push_back或insert方法,將動(dòng)態(tài)生成的控件對(duì)象裝入容器中,利用Vector提供的訪問容器中元素的方法訪問成員對(duì)象,或者利用Vector提供的pop_back或erase方法刪除容器中的元素。下面還是以動(dòng)態(tài)按鈕數(shù)組(BVec)為例,詳細(xì)說明基于Vector的動(dòng)態(tài)控件數(shù)組實(shí)現(xiàn)過程。 定義相應(yīng)類型(比如TButton)的Vector容器: vector 創(chuàng)建Button控件對(duì)象,然后添加到容器中: BVec.push_back(new TButton(this)) ; 獲取剛加入的控件對(duì)象指針,方便后續(xù)訪問: TButton* Button = *(BVec.end() - 1) ; 在刪除容器中指定的元素(比如第i個(gè)元素)時(shí),先要?jiǎng)h除容器中該成員對(duì)象的類指針?biāo)傅膶?duì)象,然后清除容器中該成員對(duì)象的值: TButton* tempButton= *(BVec.begin() + i); delete tempButton ; BVec.erase(BVec.begin() + i) ; 清空容器時(shí)先刪除容器中所有成員的類指針?biāo)傅膶?duì)象,這一點(diǎn)可以通過定義一個(gè)迭代器并遍歷容器所有元素來實(shí)現(xiàn)。然后再清除容器中所有元素的值: vector for(it = BVec.begin() ; it != BVec.end() ; it++) delete *it ; BVec.erase(BVec.begin(),BVec.end()) ; 4 基于容器對(duì)象的動(dòng)態(tài)控件數(shù)組性能分析 4.1 基于TList類的控件數(shù)組性能分析 基于TList類的控件數(shù)組應(yīng)用廣泛[9-10],實(shí)現(xiàn)方法簡(jiǎn)單,添加或刪除對(duì)象方便而高效。但是這種方法存在以下4個(gè)方面的缺陷。 安全性問題 TList類本身就是一個(gè)缺乏類型安全支持的類。從其Add方法原型int_fastcall Add(void * Item)可以看出,TList對(duì)象存儲(chǔ)并維護(hù)的是void*空指針。但是BCB編譯器會(huì)將Add函數(shù)接收到的任何類型的指針轉(zhuǎn)換為void*類型,而不做任何類型檢查。同時(shí),在引用控件數(shù)組中的一個(gè)對(duì)象時(shí),需要將一個(gè)void*類型的指針強(qiáng)制轉(zhuǎn)換為相應(yīng)控件類型的指針。如果控件數(shù)組包含多種類型的成員控件,這時(shí)就無法確定空指針該強(qiáng)制轉(zhuǎn)換為哪種類型的指針。一旦類型轉(zhuǎn)換錯(cuò)誤,那么在訪問成員控件時(shí)就會(huì)產(chǎn)生嚴(yán)重的問題。 內(nèi)存空間釋放問題 一方面,TList不能自動(dòng)刪除列表中的指針;另一方面,由于列表中的指針是空指針,刪除空指針的時(shí)候不會(huì)調(diào)用析構(gòu)器中的析構(gòu)函數(shù),那么就無法釋放內(nèi)存空間,從而造成內(nèi)存泄露。 效率問題 實(shí)驗(yàn)表明,當(dāng)TList存儲(chǔ)5 000個(gè)以上對(duì)象時(shí),效率就會(huì)大幅下降。 移植性問題 由于BCB的VCL完全是用Object Pascal語言編的,使得BCB同時(shí)獲得了Pascal和C++的強(qiáng)大功能,同時(shí)可移植性就很差。 4.2 基于DynamicArray類的控件數(shù)組性能分析 用DynamicArray實(shí)現(xiàn)控件數(shù)組,原理簡(jiǎn)單、代碼簡(jiǎn)潔,不存在類型安全和內(nèi)存釋放問題。但是,DynamicArray使用二進(jìn)制數(shù)據(jù)塊兼容Object Pascal動(dòng)態(tài)數(shù)組實(shí)現(xiàn),這會(huì)帶來系統(tǒng)瓶頸,嚴(yán)重影響運(yùn)行效率。DynamicArray的空間擴(kuò)充機(jī)制也不盡完美。當(dāng)數(shù)組長(zhǎng)度超過固定大小時(shí),就會(huì)頻繁地重新申請(qǐng)內(nèi)存空間,并將原來內(nèi)存空間中的二進(jìn)制數(shù)據(jù)塊拷貝到新申請(qǐng)的內(nèi)存空間中,然后將原空間釋放掉,這就會(huì)大大降低運(yùn)行效率。特別是如果二進(jìn)制數(shù)據(jù)塊比較大的時(shí)候,系統(tǒng)瓶頸尤其突出。另外,由于DynamicArray是VCL中的模板類,故移植性較差。 4.3 基于Vector容器類的控件數(shù)組性能分析 Vector采用線性連續(xù)的內(nèi)存空間存放數(shù)據(jù),因而隨機(jī)訪問數(shù)組元素和Vector尾部插入元素效率很高,但是在任意位置插入元素的效率比較低。為了提高效率,Vector使用了自增長(zhǎng)機(jī)制,實(shí)際上就是運(yùn)用動(dòng)態(tài)彈性內(nèi)存空間[8],隨著新元素的加入,自行擴(kuò)充內(nèi)存空間。為了合理控制空間大小、提高重新配置空間時(shí)的效率,Vector 實(shí)際配置的空間比需求空間大一些,以備將來可能的擴(kuò)充。向Vector中插入元素時(shí),首先考慮備用空間是否足夠,如果夠就插入到備用空間,否則,重新配置雙倍于現(xiàn)在容量的內(nèi)存空間,然后將原來的數(shù)據(jù)拷貝到新空間中,最后釋放原內(nèi)存空間。在控件數(shù)組中元素個(gè)數(shù)不超過1 000萬個(gè)時(shí),Vector的空間擴(kuò)充方式效率都是很高的。Vector的空間擴(kuò)充過程如圖1所示。 圖1 Vector內(nèi)存空間擴(kuò)展示意圖 另外,當(dāng)容器內(nèi)元素操作比較復(fù)雜時(shí),Vector效率不佳。 5 結(jié) 語 BCB是一個(gè)優(yōu)秀的應(yīng)用程序開發(fā)平臺(tái),其提供的VCL庫(kù)更是為開發(fā)者帶來了極大便利。VCL提供的TList和DynamicArray都可以很容易地實(shí)現(xiàn)動(dòng)態(tài)控件數(shù)組。但是,TList缺乏類型安全支持,而且Dynamic-Array也存在系統(tǒng)瓶頸問題,而且移植性都比較差,因此它們適合于小規(guī)模的局部應(yīng)用的動(dòng)態(tài)控件數(shù)組中。而標(biāo)準(zhǔn)模板庫(kù)STL提供的容器類Vector具有完備的類型安全機(jī)制,移植性比較好,執(zhí)行效率也相對(duì)很高,但是在支持?jǐn)?shù)組元素復(fù)雜操作方面欠佳,故這種方法適合于較大規(guī)模的通用動(dòng)態(tài)控件數(shù)組中??傊?,沒有哪種方法一定是最好的,具體采用什么方法要根據(jù)問題規(guī)模、應(yīng)用范圍等因素來決定。 參 考 文 獻(xiàn) [1]陳鳳祥,李汪根.C++動(dòng)態(tài)數(shù)組的實(shí)現(xiàn)與重用[J].計(jì)算機(jī)技術(shù)與發(fā)展,2010,20(2):79-82. [2]孫桂娟,張慶明,鄭全平,等.基于可變數(shù)組管理方法的震塌破壞數(shù)值仿真[J].北京理工大學(xué)學(xué)報(bào),2009,29(6):492-496. [3]喬平安.動(dòng)態(tài)統(tǒng)計(jì)圖控件的設(shè)計(jì)與實(shí)現(xiàn)[J].現(xiàn)代電子技術(shù),2006,29(6):118-120. [4]葛建芳.C++標(biāo)準(zhǔn)模板庫(kù)與代碼重用[J].南通大學(xué)學(xué)報(bào):自然科學(xué)版,2006,5(2):71-74. [5]許天然.在C++Builder中實(shí)現(xiàn)控件數(shù)組[J].瓊州大學(xué)學(xué)報(bào),2006,13(5):27-29. [6]周熙,汪紅.C++Builder環(huán)境下MVC框架結(jié)構(gòu)的應(yīng)用[J].中南民族大學(xué)學(xué)報(bào):自然科學(xué)版,2007,26(2):87-89. [7]宮護(hù)震,史云鵬,孫吉赟.一種基于STL(標(biāo)準(zhǔn)模板庫(kù))的三維數(shù)據(jù)可視化容器設(shè)計(jì)[J].現(xiàn)代電子技術(shù),2007,30(22):80-81. [8]帖軍,王小榮,金佳.移動(dòng)實(shí)時(shí)環(huán)境下的數(shù)據(jù)一致性研究[J].中南民族大學(xué)學(xué)報(bào):自然科學(xué)版,2011,30(2):92-95.. [9]景春國(guó),舒冬梅.TList對(duì)象在監(jiān)控軟件數(shù)據(jù)點(diǎn)的內(nèi)存管理和處理方面的應(yīng)用[J].現(xiàn)代電子技術(shù),2003,26(2):7-12. [10]李繼良.用TList類構(gòu)建粗糙集分類系統(tǒng)[J].計(jì)算機(jī)與現(xiàn)代化,2002(4):17-19. 作者簡(jiǎn)介: 劉 艮 男,1987年出生,山西大同人,碩士研究生。主要研究方向?yàn)樾畔踩c分布式系統(tǒng)開發(fā)。 楊玉琴 女,1987年出生,湖北武漢人,碩士研究生。主要研究方向?yàn)榉植际较到y(tǒng)開發(fā)與數(shù)字水印。 蔣天發(fā) 男,1954年出生,湖北荊門人,教授,研究生導(dǎo)師。主要研究方向?yàn)榫W(wǎng)絡(luò)安全和空間數(shù)據(jù)組織與管理。