在面向對象開發時,對實際問題分析進而抽象出一種類型,往往會考慮到2個方面:1)類型的內部成員和方法的定義描述 2)類型的多實例存取操作。其中第1點是類型本身數據結構的設計,第2點是類型容器數據結構的選擇設計。在stl中,容器有序列式和關聯式兩種,前者代表有vector,list,deque等;後者代表有set,multiset,map,multimap等,對於一個類型的第2點,我們會考慮甚至是反復權衡選擇序列容器還是關聯容器?在確定選擇了一種之後,比如序列式容器,又會進一步的思考是選擇vetor、list、還是deque?實質上對它們的選擇主要取決於應用的需要,比如要求快速訪問就選擇vetor,要求快速插入刪除就選擇list,符合隊列模型就選擇deque。如果對性能要求較高,就得選擇關聯式容器,這樣訪問、插入、刪除元素都能得到較好的性能。如果操作太頻繁,這時set,map還不行,要選擇hash_set,hash_map等。
本文僅探討序列式容器的選擇應用,對一個類型而言,既選擇了序列式容器,就意味在vector,list,deque間選擇(這裡要說明的是,stack和queue雖然也是序列式容器,但從真正嚴格意義上講,它們只是一種適配器)那麼有沒有辦法作一個通用的包裝類提供給開發者,讓開發者自己根據應用決定選擇具體的容器類型呢?同時這個包裝又不影響原容器的接口使用。關於包裝類的實現,代碼描述如下
1#ifndef _STL_COLLECTION_H
2#define _STL_COLLECTION_H
3
4#include <memory>
5#include <vector>
6
7 /**
8 @class STLCollection
9 @brief 基於STL序列容器實現的通用集合類
10
11 * 提供以索引作為外參的以下公共通用接口
12 * add --- 向前或向後增加單個元素
13 * insert --- 插入單個元素
14 * erase --- 刪除單個或多個元素
15 * set --- 修改某個元素
16 * get --- 獲取某個元素
17 * front --- 獲取第一個元素
18 * back --- 獲取最後一個元素
19*/
20template<typename T,template<class T,class U > class C = std::vector,template <class T> class U = std::allocator>
21class STLCollection : public C<T,U<T> >
22{
23 typedef U<T> Allocator;
24 typedef C<T,Allocator> base;
25
26public:
27 //為使用方便,重新定義實現構造函數及拷貝構造函數,但賦值拷貝可以不用重新定義實現
28 STLCollection()
29 {
30 }
31 explicit STLCollection(const Allocator& al)
32 :base(al)
33 {
34 }
35 explicit STLCollection(size_t n)
36 :base(n)
37 {
38 }
39 STLCollection(size_t n,const T& t)
40 :base(n,t)
41 {
42 }
43 STLCollection(size_t n,const T& t,const Allocator& al)
44 :base(n,t,al)
45 {
46 }
47 STLCollection(const STLCollection& right)
48 :base(right)
49 {
50 }
51
52 template<class InputIterator>
53 STLCollection(InputIterator first,InputIterator last)
54 :base(first,last)
55 {
56 }
57
58 template<class InputIterator>
59 STLCollection(InputIterator first,InputIterator last,const Allocator& al)
60 :base(first,last,al)
61 {
62 }
63
64public:
65 //使基類的同名函數erase,insert,front,back可見
66 using base::erase;
67 using base::insert;
68 using base::front;
69 using base::back;
70
71 void add(const T& t,bool append = true)
72 {
73 if (append)
74 base::insert(base::end(),t);
75 else
76 base::insert(base::begin(),t);
77 }
78 void insert(size_t index,const T& t)
79 {
80 insert_impl(index,t,typename std::iterator_traits<typename base::iterator>::iterator_category());
81 }
82 void erase(size_t index)
83 {
84 erase_impl(index,typename std::iterator_traits<typename base::iterator>::iterator_category());
85 }
86 void erase(size_t beg,size_t end)
87 {
88 erase_impl(beg,end,typename std::iterator_traits<typename base::iterator>::iterator_category());
89 }
90 void set(size_t index,const T& t)
91 {
92 T* p = get(index);
93 if (p) *p = t;
94 }
95 T* get(size_t index)
96 {
97 return get_impl(index,typename std::iterator_traits<typename base::iterator>::iterator_category());
98 }
99 const T* get(size_t index) const
100 {
101 return get_impl(index,typename std::iterator_traits<typename base::iterator>::iterator_category());
102 }
103 T* front()
104 {
105 if (base::empty()) return NULL;
106 return &base::front();
107 }
108 const T* front() const
109 {
110 if (base::empty()) return NULL;
111 return &base::front();
112 }
113 T* back()
114 {
115 if (base::empty()) return NULL;
116 return &base::back();
117 }
118 const T* back() const
119 {
120 if (base::empty()) return NULL;
121 return &base::back();
122 }
123 bool is_empty() const
124 {
125 return base::empty();
126 }
127
128private:
129 /*************************************************************************************
130 下面函數僅作內部實現,需要注意以下幾點
131 (1) 不讓其子類和外部可見,故使用private訪問控制
132 (2) 考慮到子類可能會使用using指令來引用,如果為重載形式,子類using引用同名函數
133 會因為private出錯而不能引用public同名函數,故特命名為xxx_impl而非重載形式
134 *************************************************************************************/
135 void insert_impl(size_t index,const T& t,std::random_access_iterator_tag tag)
136 {
137 if (index < base::size())
138 {
139 base::insert(base::begin()+index,t);
140 }
141 }
142 void insert_impl(size_t index,const T& t,std::input_iterator_tag tag)
143 {
144 if (index < base::size())
145 {
146 typename base::iterator it = base::begin();
147 while(index--) ++it;
148 base::insert(it,t);
149 }
150 }
151 void erase_impl(size_t index,std::random_access_iterator_tag tag)
152 {
153 if (index < base::size())
154 {
155 base::erase(base::begin()+index);
156 }
157 }
158 void erase_impl(size_t index,std::input_iterator_tag tag)
159 {
160 if (index < base::size())
161 {
162 typename base::iterator it = base::begin();
163 while(index--) ++it;
164 base::erase(it);
165 }
166 }
167 void erase_impl(size_t beg,size_t end,std::random_access_iterator_tag tag)
168 {
169 end = std::min(end,base::size());
170 if (beg < end)
171 {
172 base::erase(base::begin()+beg,base::begin()+end);
173 }
174 }
175 void erase_impl(size_t beg,size_t end,std::input_iterator_tag tag)
176 {
177 end = std::min(end,base::size());
178 if (beg < end)
179 {
180 typename base::iterator it = base::begin();
181 while(beg++ < end) it = base::erase(it);
182 }
183 }
184 T* get_impl(size_t index,std::random_access_iterator_tag tag)
185 {
186 if (index>=base::size())
187 return NULL;
188 return &(*(base::begin()+index));
189 }
190 const T* get_impl(size_t index,std::random_access_iterator_tag tag) const
191 {
192 if (index>=base::size())
193 return NULL;
194 return &(*(base::begin()+index));
195 }
196 T* get_impl(size_t index,std::input_iterator_tag tag)
197 {
198 if (index>=base::size())
199 return NULL;
200 typename base::iterator it = base::begin();
201 while (index--) ++it;
202 return &(*it);
203 }
204 const T* get_impl(size_t index,std::input_iterator_tag tag) const
205 {
206 if (index>=base::size())
207 return NULL;
208 typename base::const_iterator it = base::begin();
209 while(index--) ++it;
210 return &(*it);
211 }
212};
213
214#endif
這樣一來,由於STLCollection類提供了通用的操作接口,在應用時如果想切換改變為另一種容器,只需改變第2個模板參數即可,其它部分代碼都不用改變,大大方便了程序的維護擴展,還可以繼承STLCollection類,實現自己特殊的集合類,比如元素類型是_variant_t類型,代碼描述如下
1#ifndef _VARIANT_COLLECTION_H
2#define _VARIANT_COLLECTION_H
3
4#include <comutil.h>
5#pragma comment(lib,"comsuppw.lib")
6
7class variant_collection : public STLCollection<_variant_t>
8{
9 typedef STLCollection<_variant_t> base;
10
11public:
12 using base::add;
13 using base::insert;
14 using base::set;
15
16 void add(signed char val)
17 {
18 add<signed char>(val);
19 }
20 void add(unsigned char val)
21 {
22 add<unsigned char>(val);
23 }
24 void add(short val)
25 {
26 add<short>(val);
27 }
28 void add(unsigned short val)
29 {
30 add<unsigned short>(val);
31 }
32 void add(int val)
33 {
34 add<int>(val);
35 }
36 void add(unsigned int val)
37 {
38 add<unsigned int>(val);
39 }
40 void add(float val)
41 {
42 add<float>(val);
43 }
44 void add(double val)
45 {
46 add<double>(val);
47 }
48 void add(const char* val)
49 {
50 add<const char*>(val);
51 }
52
53 void insert(size_t index,signed char val)
54 {
55 insert<signed char>(index,val);
56 }
57 void insert(size_t index,unsigned char val)
58 {
59 insert<unsigned char>(index,val);
60 }
61 void insert(size_t index,short val)
62 {
63 insert<short>(index,val);
64 }
65 void insert(size_t index,unsigned short val)
66 {
67 insert<unsigned short>(index,val);
68 }
69 void insert(size_t index,int val)
70 {
71 insert<int>(index,val);
72 }
73 void insert(size_t index,unsigned int val)
74 {
75 insert<unsigned int>(index,val);
76 }
77 void insert(size_t index,float val)
78 {
79 insert<float>(index,val);
80 }
81 void insert(size_t index,double val)
82 {
83 insert<double>(index,val);
84 }
85 void insert(size_t index,const char* val)
86 {
87 insert<const char*>(index,val);
88 }
89
90 void set(size_t index,signed char val)
91 {
92 set<signed char>(index,val);
93 }
94 void set(size_t index,unsigned char val)
95 {
96 set<unsigned char>(index,val);
97 }
98 void set(size_t index,short val)
99 {
100 set<short>(index,val);
101 }
102 void set(size_t index,unsigned short val)
103 {
104 set<unsigned short>(index,val);
105 }
106 void set(size_t index,int val)
107 {
108 set<int>(index,val);
109 }
110 void set(size_t index,unsigned int val)
111 {
112 set<unsigned int>(index,val);
113 }
114 void set(size_t index,float val)
115 {
116 set<float>(index,val);
117 }
118 void set(size_t index,double val)
119 {
120 set<double>(index,val);
121 }
122 void set(size_t index,const char* val)
123 {
124 set<const char*>(index,val);
125 }
126
127protected:
128 template<typename T>
129 void add(T val)
130 {
131 _variant_t var(val);
132 base::add(var);
133 }
134 template<typename T>
135 void insert(size_t index,T val)
136 {
137 _variant_t var(val);
138 base::insert(index,var);
139 }
140 template<typename T>
141 void set(size_t index,T val)
142 {
143 _variant_t* p_var = base::get(index);
144 if (p_var) *p_var = val;
145 }
146};
147
148#endif