The vector is intentionally made to look like a souped-up array, since it has array-style indexing but also can expand dynamically. vector is so fundamentally useful that it was introduced in a very primitive way early in this book, and used quite regularly in previous examples. This section will give a more in-depth look at vector.
To achieve maximally-fast indexing and iteration, the vector maintains its storage as a single contiguous array of objects. This is a critical point to observe in understanding the behavior of vector. It means that indexing and iteration are lighting-fast, being basically the same as indexing and iterating over an array of objects. But it also means that inserting an object anywhere but at the end (that is, appending) is not really an acceptable operation for a vector. It also means that when a vector runs out of pre-allocated storage, in order to maintain its contiguous array it must allocate a whole new (larger) chunk of storage elsewhere and copy the objects to the new storage. This has a number of unpleasant side effects.
The deque (double-ended-queue, pronounced “deck”) is the basic sequence container optimized for adding and removing elements from either end. It also allows for reasonably fast random access – it has an operator[ ] like vector. However, it does not have vector’s constraint of keeping everything in a single sequential block of memory. Instead, deque uses multiple blocks of sequential storage (keeping track of all the blocks and their order in a mapping structure). For this reason the overhead for a deque to add or remove elements at either end is very low. In addition, it never needs to copy and destroy contained objects during a new storage allocation (like vector does) so it is far more efficient than vector if you are adding an unknown quantity of objects. This means that vector is the best choice only if you
have a pretty good idea of how many objects you need. In addition, many of the programs shown earlier in this book that use vector and push_back( ) might be more efficient with a deque. The interface to deque is only slightly different from a vector (deque has a push_front( ) and pop_front( ) while vector does not, for example) so converting code from using vector to using deque is almost trivial.
A list is implemented as a doubly-linked list and is thus designed for rapid insertion and removal of elements in the middle of the sequence (whereas for vector and deque this is a much more costly operation). A list is so slow when randomly accessing elements that it does not have an operator[ ]. It’s best used when you’re traversing a sequence, in order, from beginning to end (or end to beginning) rather than choosing elements randomly from the middle. Even then the traversal is significantly slower than either a vector or a deque, but if
you aren’t doing a lot of traversals that won’t be your bottleneck.
Another thing to be aware of with a list is the memory overhead of each link, which requires a forward and backward pointer on top of the storage for the actual object. Thus a list is a better choice when you have larger objects that you’ll be inserting and removing from the middle of the list. It’s better not to use a list if you think you might be traversing it a lot, looking for objects, since the amount of time it takes to get from the beginning of the list – which is the only place you can start unless you’ve already got an iterator to somewhere you know is closer to your destination – to the object of interest is proportional to the number of objects between the beginning and that object.
The objects in a list never move after they are created; “moving” a list element means changing the links, but never copying or assigning the actual objects. This means that a held iterator never moves when you add new things to a list as it was demonstrated to do in vector.
Performance
1
#include
<
vector
>
2 #include < queue >
3 #include < list >
4 #include < iostream >
5 #include < string >
6 #include < typeinfo >
7 #include < ctime >
8 #include < cstdlib >
9 using namespace std;
10
11 class FixedSize
12 {
13 int x[ 20 ];
14 // Automatic generation of default constructor,
15 // copy-constructor and operator=
16 } fs;
17
18 template < class Cont >
19 struct InsertBack
20 {
21 void operator ()(Cont & c, long count)
22 {
23 for ( long i = 0 ; i < count; i ++ )
24 c.push_back(fs);
25 }
26
27 char * testName() { return " InsertBack " ; }
28 };
29
30 template < class Cont >
31 struct InsertFront
32 {
33 void operator ()(Cont & c, long count)
34 {
35 long cnt = count * 10 ;
36 for ( long i = 0 ; i < cnt; i ++ )
37 c.push_front(fs);
38 }
39
40 char * testName() { return " InsertFront " ; }
41 };
42
43 template < class Cont >
44 struct InsertMiddle
45 {
46 void operator ()(Cont & c, long count)
47 {
48 typename Cont::iterator it;
49 long cnt = count / 10 ;
50 for ( long i = 0 ; i < cnt; i ++ )
51 {
52 // Must get the iterator every time to keep
53 // from causing an access violation with
54 // vector. Increment it to put it in the
55 // middle of the container:
56 it = c.begin();
57 it ++ ;
58 c.insert(it, fs);
59 }
60 }
61
62 char * testName() { return " InsertMiddle " ; }
63 };
64
65 template < class Cont >
66 struct RandomAccess
67 { // Not for list
68 void operator ()(Cont & c, long count)
69 {
70 int sz = c.size();
71 long cnt = count * 100 ;
72 for ( long i = 0 ; i < cnt; i ++ )
73 c[rand() % sz];
74 }
75 char * testName() { return " RandomAccess " ; }
76 };
77
78 template < class Cont >
79 struct Traversal
80 {
81 void operator ()(Cont & c, long count)
82 {
83 long cnt = count / 100 ;
84 for ( long i = 0 ; i < cnt; i ++ )
85 {
86 typename Cont::iterator it = c.begin(),
87 end = c.end();
88 while (it != end) it ++ ;
89 }
90 }
91
92 char * testName() { return " Traversal " ; }
93 };
94
95 template < class Cont >
96 struct Swap
97 {
98 void operator ()(Cont & c, long count)
99 {
100 int middle = c.size() / 2 ;
101 typename Cont::iterator it = c.begin(),
102 mid = c.begin();
103 it ++ ; // Put it in the middle
104 for ( int x = 0 ; x < middle + 1 ; x ++ )
105 mid ++ ;
106 long cnt = count * 10 ;
107 for ( long i = 0 ; i < cnt; i ++ )
108 swap( * it, * mid);
109 }
110
111 char * testName() { return " Swap " ; }
112 };
113
114 template < class Cont >
115 struct RemoveMiddle
116 {
117 void operator ()(Cont & c, long count)
118 {
119 long cnt = count / 10 ;
120 if (cnt > c.size())
121 {
122 cout << " RemoveMiddle: not enough elements " << endl;
123 return ;
124 }
125 for ( long i = 0 ; i < cnt; i ++ )
126 {
127 typename Cont::iterator it = c.begin();
128 it ++ ;
129 c.erase(it);
130 }
131 }
132
133 char * testName() { return " RemoveMiddle " ; }
134 };
135
136 template < class Cont >
137 struct RemoveBack
138 {
139 void operator ()(Cont & c, long count)
140 {
141 long cnt = count * 10 ;
142 if (cnt > c.size())
143 {
144 cout << " RemoveBack: not enough elements " << endl;
145 return ;
146 }
147 for ( long i = 0 ; i < cnt; i ++ )
148 c.pop_back();
149 }
150
151 char * testName() { return " RemoveBack " ; }
152 };
153
154 template < class Op, class Container >
155 void measureTime(Op f, Container & c, long count)
156 {
157 string id(typeid(f).name());
158 bool Deque = id.find( " deque " ) != string ::npos;
159 bool List = id.find( " list " ) != string ::npos;
160 bool Vector = id.find( " vector " ) != string ::npos;
161 string cont = Deque ? " deque " : List ? " list " : Vector ? " vector " : " unknown " ;
162 cout << f.testName() << " for " << cont << " : " ;
163 // Standard C library CPU ticks:
164 clock_t ticks = clock();
165 f(c, count); // Run the test
166 ticks = clock() - ticks;
167 cout << ticks << endl;
168 }
169
170 typedef deque < FixedSize > DF;
171 typedef list < FixedSize > LF;
172 typedef vector < FixedSize > VF;
173
174 int main( int argc, char * argv[])
175 {
176 srand(time( 0 ));
177 long count = 1000 ;
178 if (argc >= 2 )
179 count = atoi(argv[ 1 ]);
180 DF deq;
181 LF lst;
182 VF vec, vecres;
183 vecres.reserve(count); // Preallocate storage
184 measureTime(InsertBack < VF > (), vec, count);
185 measureTime(InsertBack < VF > (), vecres, count);
186 measureTime(InsertBack < DF > (), deq, count);
187 measureTime(InsertBack < LF > (), lst, count);
188 // Can't push_front() with a vector:
189 // ! measureTime(InsertFront<VF>(), vec, count);
190 measureTime(InsertFront < DF > (), deq, count);
191 measureTime(InsertFront < LF > (), lst, count);
192 measureTime(InsertMiddle < VF > (), vec, count);
193 measureTime(InsertMiddle < DF > (), deq, count);
194 measureTime(InsertMiddle < LF > (), lst, count);
195 measureTime(RandomAccess < VF > (), vec, count);
196 measureTime(RandomAccess < DF > (), deq, count);
197 // Can't operator[] with a list:
198 // ! measureTime(RandomAccess<LF>(), lst, count);
199 measureTime(Traversal < VF > (), vec, count);
200 measureTime(Traversal < DF > (), deq, count);
201 measureTime(Traversal < LF > (), lst, count);
202 measureTime(Swap < VF > (), vec, count);
203 measureTime(Swap < DF > (), deq, count);
204 measureTime(Swap < LF > (), lst, count);
205 measureTime(RemoveMiddle < VF > (), vec, count);
206 measureTime(RemoveMiddle < DF > (), deq, count);
207 measureTime(RemoveMiddle < LF > (), lst, count);
208 vec.resize(vec.size() * 10 ); // Make it bigger
209 measureTime(RemoveBack < VF > (), vec, count);
210 measureTime(RemoveBack < DF > (), deq, count);
211 measureTime(RemoveBack < LF > (), lst, count);
212
213 cin. get ();
214 } /// :~
2 #include < queue >
3 #include < list >
4 #include < iostream >
5 #include < string >
6 #include < typeinfo >
7 #include < ctime >
8 #include < cstdlib >
9 using namespace std;
10
11 class FixedSize
12 {
13 int x[ 20 ];
14 // Automatic generation of default constructor,
15 // copy-constructor and operator=
16 } fs;
17
18 template < class Cont >
19 struct InsertBack
20 {
21 void operator ()(Cont & c, long count)
22 {
23 for ( long i = 0 ; i < count; i ++ )
24 c.push_back(fs);
25 }
26
27 char * testName() { return " InsertBack " ; }
28 };
29
30 template < class Cont >
31 struct InsertFront
32 {
33 void operator ()(Cont & c, long count)
34 {
35 long cnt = count * 10 ;
36 for ( long i = 0 ; i < cnt; i ++ )
37 c.push_front(fs);
38 }
39
40 char * testName() { return " InsertFront " ; }
41 };
42
43 template < class Cont >
44 struct InsertMiddle
45 {
46 void operator ()(Cont & c, long count)
47 {
48 typename Cont::iterator it;
49 long cnt = count / 10 ;
50 for ( long i = 0 ; i < cnt; i ++ )
51 {
52 // Must get the iterator every time to keep
53 // from causing an access violation with
54 // vector. Increment it to put it in the
55 // middle of the container:
56 it = c.begin();
57 it ++ ;
58 c.insert(it, fs);
59 }
60 }
61
62 char * testName() { return " InsertMiddle " ; }
63 };
64
65 template < class Cont >
66 struct RandomAccess
67 { // Not for list
68 void operator ()(Cont & c, long count)
69 {
70 int sz = c.size();
71 long cnt = count * 100 ;
72 for ( long i = 0 ; i < cnt; i ++ )
73 c[rand() % sz];
74 }
75 char * testName() { return " RandomAccess " ; }
76 };
77
78 template < class Cont >
79 struct Traversal
80 {
81 void operator ()(Cont & c, long count)
82 {
83 long cnt = count / 100 ;
84 for ( long i = 0 ; i < cnt; i ++ )
85 {
86 typename Cont::iterator it = c.begin(),
87 end = c.end();
88 while (it != end) it ++ ;
89 }
90 }
91
92 char * testName() { return " Traversal " ; }
93 };
94
95 template < class Cont >
96 struct Swap
97 {
98 void operator ()(Cont & c, long count)
99 {
100 int middle = c.size() / 2 ;
101 typename Cont::iterator it = c.begin(),
102 mid = c.begin();
103 it ++ ; // Put it in the middle
104 for ( int x = 0 ; x < middle + 1 ; x ++ )
105 mid ++ ;
106 long cnt = count * 10 ;
107 for ( long i = 0 ; i < cnt; i ++ )
108 swap( * it, * mid);
109 }
110
111 char * testName() { return " Swap " ; }
112 };
113
114 template < class Cont >
115 struct RemoveMiddle
116 {
117 void operator ()(Cont & c, long count)
118 {
119 long cnt = count / 10 ;
120 if (cnt > c.size())
121 {
122 cout << " RemoveMiddle: not enough elements " << endl;
123 return ;
124 }
125 for ( long i = 0 ; i < cnt; i ++ )
126 {
127 typename Cont::iterator it = c.begin();
128 it ++ ;
129 c.erase(it);
130 }
131 }
132
133 char * testName() { return " RemoveMiddle " ; }
134 };
135
136 template < class Cont >
137 struct RemoveBack
138 {
139 void operator ()(Cont & c, long count)
140 {
141 long cnt = count * 10 ;
142 if (cnt > c.size())
143 {
144 cout << " RemoveBack: not enough elements " << endl;
145 return ;
146 }
147 for ( long i = 0 ; i < cnt; i ++ )
148 c.pop_back();
149 }
150
151 char * testName() { return " RemoveBack " ; }
152 };
153
154 template < class Op, class Container >
155 void measureTime(Op f, Container & c, long count)
156 {
157 string id(typeid(f).name());
158 bool Deque = id.find( " deque " ) != string ::npos;
159 bool List = id.find( " list " ) != string ::npos;
160 bool Vector = id.find( " vector " ) != string ::npos;
161 string cont = Deque ? " deque " : List ? " list " : Vector ? " vector " : " unknown " ;
162 cout << f.testName() << " for " << cont << " : " ;
163 // Standard C library CPU ticks:
164 clock_t ticks = clock();
165 f(c, count); // Run the test
166 ticks = clock() - ticks;
167 cout << ticks << endl;
168 }
169
170 typedef deque < FixedSize > DF;
171 typedef list < FixedSize > LF;
172 typedef vector < FixedSize > VF;
173
174 int main( int argc, char * argv[])
175 {
176 srand(time( 0 ));
177 long count = 1000 ;
178 if (argc >= 2 )
179 count = atoi(argv[ 1 ]);
180 DF deq;
181 LF lst;
182 VF vec, vecres;
183 vecres.reserve(count); // Preallocate storage
184 measureTime(InsertBack < VF > (), vec, count);
185 measureTime(InsertBack < VF > (), vecres, count);
186 measureTime(InsertBack < DF > (), deq, count);
187 measureTime(InsertBack < LF > (), lst, count);
188 // Can't push_front() with a vector:
189 // ! measureTime(InsertFront<VF>(), vec, count);
190 measureTime(InsertFront < DF > (), deq, count);
191 measureTime(InsertFront < LF > (), lst, count);
192 measureTime(InsertMiddle < VF > (), vec, count);
193 measureTime(InsertMiddle < DF > (), deq, count);
194 measureTime(InsertMiddle < LF > (), lst, count);
195 measureTime(RandomAccess < VF > (), vec, count);
196 measureTime(RandomAccess < DF > (), deq, count);
197 // Can't operator[] with a list:
198 // ! measureTime(RandomAccess<LF>(), lst, count);
199 measureTime(Traversal < VF > (), vec, count);
200 measureTime(Traversal < DF > (), deq, count);
201 measureTime(Traversal < LF > (), lst, count);
202 measureTime(Swap < VF > (), vec, count);
203 measureTime(Swap < DF > (), deq, count);
204 measureTime(Swap < LF > (), lst, count);
205 measureTime(RemoveMiddle < VF > (), vec, count);
206 measureTime(RemoveMiddle < DF > (), deq, count);
207 measureTime(RemoveMiddle < LF > (), lst, count);
208 vec.resize(vec.size() * 10 ); // Make it bigger
209 measureTime(RemoveBack < VF > (), vec, count);
210 measureTime(RemoveBack < DF > (), deq, count);
211 measureTime(RemoveBack < LF > (), lst, count);
212
213 cin. get ();
214 } /// :~