A Presentation of the STL Vector Container
2012-09-07 12:21
435 查看
Introduction
This article aims to introduce thestd::vectoras well as cover some of the most common
vectormember
functions and how to use them properly. The article will also discuss predicates and function pointers as used in common iterator algorithms such as
remove_if()and
for_each().
After reading this article, the reader should be able to use the
vectorcontainer effectively and should find no use for dynamic C-style arrays again.
Vector Overview
Thevectoris part of the C++ Standard Template Library (STL); an amalgamation of general-purpose, templatized classes and functions that
implement a plethora of common data structures and algorithms. The
vectoris considered a container class. Like containers in real-life, these containers are
objects that are designed to hold other objects. In short, a
vectoris a dynamic array designed to hold objects of any type, and capable of growing and shrinking
as needed.
In order to use
vector, you need to include the following header file:
Collapse | Copy
Code
#include <vector>
The vectoris part of the
stdnamespace, so you need to qualify the name. This can be accomplished as shown here:
Collapse | Copy
Code
using std::vector; vector<int> vInts;
or you can fully qualify the name like this:
Collapse | Copy
Code
std::vector<int> vInts;
I do however suggest that you refrain from declaring global namespaces such as:
Collapse | Copy
Code
using namespace std;
This pollutes the global namespace and may cause problems in later implementations. As for the interface to the
vectorcontainer, I have
listed the member functions and operators of
vectorin the tables below.
Vector Member Functions 1
Function | Description |
assign | Erases a vectorand copies the specified elements to the empty vector. |
at | Returns a reference to the element at a specified location in the vector. |
back | Returns a reference to the last element of the vector. |
begin | Returns a random-access iterator to the first element in the container. |
capacity | Returns the number of elements that the vectorcould contain without allocating more storage. |
clear | Erases the elements of the vector. |
empty | Tests if the vectorcontainer is empty. |
end | Returns a random-access iterator that points just beyond the end of thevector. |
erase | Removes an element or a range of elements in a vectorfrom specified positions. |
front | Returns a reference to the first element in a vector. |
get_allocator | Returns an object to the allocator class used by a vector. |
insert | Inserts an element or a number of elements into the vectorat a specified position. |
max_size | Returns the maximum length of the vector. |
pop_back | Deletes the element at the end of the vector. |
push_back | Adds an element to the end of the vector. |
rbegin | Returns an iterator to the first element in a reversed vector. |
rend | Returns an iterator to the end of a reversed vector. |
resize | Specifies a new size for a vector. |
reserve | Reserves a minimum length of storage for a vectorobject. |
size | Returns the number of elements in the vector. |
swap | Exchanges the elements of two vectors. |
vector | Constructs a vectorof a specific size or with elements of a specific value or with a specific allocator or as a copy of some other vector. |
Vector Operators 1
Operator | Description |
operator[] | Returns a reference to the vectorelement at a specified position. |
Constructing a Vector
There are several constructors provided for thevectorcontainer. Here are some of the most common.
Construct an empty vectorto hold objects of type Widget:
Collapse | Copy
Code
vector<Widget> vWidgets; // ------ // | // |- Since vectoris a container, its member functions // operate on iterators and the container itself so // it can hold objects of any type.
Construct a vectorto hold 500 Widgets:
Collapse | Copy
Code
vector<Widget> vWidgets(500);
Construct a vectorto hold 500 Widgets initialized to 0:
Collapse | Copy
Code
vector<Widget> vWidgets(500, Widget(0));
Construct a vectorof Widgets from another vectorof Widgets:
Collapse | Copy
Code
vector<Widget> vWidgetsFromAnother(vWidgets);
Adding Elements to a Vector
The default method of adding elements to avectoris by using
push_back().
The
push_back()member function is responsible for adding elements to the end of a
vectorand
allocating memory as needed. For example, to add 10
Widgets to a
vector<Widget>,
you would use the following code:
Collapse | Copy
Code
for(int i= 0;i<10; i++) vWidgets.push_back(Widget(i));
Getting the Number of Elements in a Vector
Many times it is necessary to know how many elements are in yourvector(if there are any at all). Remember, the
vectoris
dynamic, and the number of allocations and
push_back()s are usually determined by a user file or some other data source. To test if your
vectorcontains
anything at all, you call
empty(). To get the number of elements, you call
size().
For example, if you wanted to initialize a variable to -1 if your
vector,
v,
is empty or to the number of elements in
vif it's not, you would use the following code:
Collapse | Copy
Code
int nSize = v.empty() ? -1 : static_cast<int>(v.size());
Accessing Elements in a Vector
There are two ways to access objects stored in avector.
vector::at()
vector::operator[]
The
operator[]is supported mainly for compatibility with a legacy C code base. It operates the same way a standard C-style array
operator[]works.
It is always preferred to use the
at()member function, however, as
at()is
bounds-checked, and will throw an exception if we attempt to access a location beyond the
vectorlimits. However,
operator[]could
care less and can cause some serious bugs as we will now demonstrate.
Consider the following code:
Collapse | Copy
Code
vector<int> v; v.reserve(10); for(int i=0; i<7; i++) v.push_back(i); try { int iVal1 = v[7]; // not bounds checked - will not throw int iVal2 = v.at(7); // bounds checked - will throw if out of range } catch(const exception& e) { cout << e.what(); }
Now, just because we reserved space for 10
ints doesn't mean they are initialized or even defined. This scenario is best illustrated in
the figure below.
You can try this code on your own and observe the results or see a similar situation in the demo. The bottom line: use
at()whenever you
can.
Removing Elements from a Vector
Getting things into avectoris fairly easy, but getting them out can be subtly tricky. First of all, the only member functions a
vectorhas,
to get rid of elements are
erase(),
pop_back(), and
clear().
Now these are sufficient if you know: which elements you need to remove; if you only need to remove the last element; or if you want to remove all of the elements, respectively. Now before you go off writing some crazy loop to flag which elements you want
to erase and brute-force yourself an application-specific implementation, sit back, take a deep breath, and think to yourself: "S...T...L...".
The remove_if() Algorithm
Now, this is where we get into some fun stuff. To useremove_if(), you will need to include the following file:
Collapse | Copy
Code
#include <algorithm>
remove_if()takes 3 parameters:
iterator _First: An iterator pointing to the first element in the range on which to operate.
iterator _Last: An iterator pointing to the last element in the range on which to operate.
predicate _Pred: A predicate to apply to the iterator being evaluated.
Predicates
Predicates are basically pointers to a function or an actual function object that returns a yes/no answer to a user defined evaluation of that object. The function object is required to supply a function call operator,operator()().
In the case of
remove_if(), it is often useful to supply a function object derived from
unary_functionto
allow the user to pass data to the predicate for evaluation.
For example, consider a scenario in which you want to remove elements from a
vector<CString>based on certain matching criteria, i.e. if
the string contains a value, starts with a value, ends with a value or is a value. First you would set up a data structure that would hold the relevant data, similar to the following code:
Collapse | Copy
Code
#include <functional> enum findmodes { FM_INVALID = 0, FM_IS, FM_STARTSWITH, FM_ENDSWITH, FM_CONTAINS }; typedef struct tagFindStr { UINT iMode; CString szMatchStr; } FindStr; typedef FindStr* LPFINDSTR;
You could then proceed to implement the predicate as shown:
Collapse | Copy
Code
class FindMatchingString : public std::unary_function<CString, bool> { public: FindMatchingString(const LPFINDSTR lpFS) : m_lpFS(lpFS) {} bool operator()(CString& szStringToCompare) const { bool retVal = false; switch(m_lpFS->iMode) { case FM_IS: { retVal = (szStringToCompare == m_lpFDD->szMatchStr); break; } case FM_STARTSWITH: { retVal = (szStringToCompare.Left(m_lpFDD->szMatchStr.GetLength()) == m_lpFDD->szWindowTitle); break; } case FM_ENDSWITH: { retVal = (szStringToCompare.Right(m_lpFDD->szMatchStr.GetLength()) == m_lpFDD->szMatchStr); break; } case FM_CONTAINS: { retVal = (szStringToCompare.Find(m_lpFDD->szMatchStr) != -1); break; } } return retVal; } private: LPFINDSTR m_lpFS; };
With this implementation, you could effectively remove strings from a
vectoras shown below:
Collapse | Copy
Code
// remove all strings containing the value of // szRemove from vector<CString> vs. FindStr fs; fs.iMode = FM_CONTAINS; fs.szMatchStr = szRemove; vs.erase(std::remove_if(vs.begin(), vs.end(), FindMatchingString(&fs)), vs.end());
What remove_if() Really Does
You may be wondering why I have a call toerase()when I am calling
remove_if()in
the above example. The reason for this is quite subtle and not intuitive for people not familiar with iterators or the STL algorithms. Since
remove(),
remove_if(),
and all the remove cousins operate only on a range of iterators, they cannot operate on the container's internals. The call to
remove_if()actually shuffles around
the elements of the container being operated on. Consider the above example if:
szRemove= "o".
vs= the diagram shown below.
After observing the results, one can see that
remove_if()actually returns a forward iterator addressing the new end position of the modified
range, one past the final element of the remnant sequence, free of the specified value. The remaining elements may or may not have their original value, to be safe, always assume they are unknown.
The call to
erase()then is responsible for actually disposing off the "removed" elements. Notice in the above example that we pass the
range from the result of
remove_if()through
vs.end()to
erase().
Shrinking a Bloated Vector
Many times, after lots of data removal or a liberal call toreserve(), you can end up with a
vectorwhose
internals are far larger than they need to be. In this case, it is often desirable to "shrink" the
vectorto fit the size of the data. However, the
resize()member
function is only capable of increasing the capacity of the
vector. The only member capable of changing the size of the internal buffer is
clear(),
however this has some significant drawbacks like destroying everything in the
vector. So how can one solve this problem? Well, let's attempt a first pass.
Remember that we can construct a
vectorfrom another
vector.
Let's see what happens. Consider we have a
vector,
v, with a capacity
of 1000. We then make a call to
size()and realize we only have 7 elements in the
vector.
Wow, we're wasting lots of memory! Let's try and make a new
vectorfrom our current one.
Collapse | Copy
Code
std::vector<CString> vNew(v); cout << vNew.capacity();
It turns out
vNew.capacity()returns 7. The new
vectoronly
allocates enough space for what it gets from
v. Now, I don't want to get rid of
v,
because I may be using it all over the place. What if I try and
swap()the internals of
vand
vNew?
Collapse | Copy
Code
vNew.swap(v); cout << vNew.capacity(); cout << v.capacity();
Interesting:
vNew.capacity()is now 1000, and
v.capacity()is
7. Sweet, it worked.
Yes, it may have worked, but that's not an elegant solution, now is it? How can we make this swap trick have some flair? Do we really need to name our temporary variable? Lets try another approach. Consider this code:
Collapse | Copy
Code
std::vector<CString>(v).swap(v);
Can you see what we did there? We have created a temporary unnamed variable instead of a named one and did the
swap()all in one step. Much
more elegant. Now when the temporary variable goes out of scope, it will take its bloated internal buffer with it, and then we're left with a nice trim
v.
Putting it All Together
In the sample applications, you can see how to implement the topics discussed here. The console application is a step-by-step illustration of these examples in action, and the MFC application is mainly a demonstration of using STL,algorithms, and predicates with MFC.
Conclusion
I hope this article serves as a valuable reference and guideline for developers using the STLvectorcontainer. I would also hope that
those who were skeptical about using the
vectorbefore reading this article are now undaunted and ready to throw away their C-style arrays and try it out.
References
Plauger, P.J. Standard C++ Library Reference. February, 2003. MSDN.Schildt, Herbert. C++ from the Ground Up, Second Edition. Berkeley: 1998.
Sutter, Herb. More Exceptional C++. Indianapolis: 2002.
License
This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.A list of licenses authors might use can be found here
http://www.codeproject.com/Articles/5378/A-Presentation-of-the-STL-Vector-Container
相关文章推荐
- A Presentation of the STL Vector Container
- Question 6: Which of the following are container adapters in the STL (Standard Template Library) in C++?
- 深入研究 STL Deque 容器-An In-Depth Study of the STL Deque Container (By Nitron)
- 深入研究 STL Deque 容器-An In-Depth Study of the STL Deque Container (By Nitron)
- An Example about the use of map Container in C++ STL / c++ map容器的用法
- vector,list,deque,set,map of STL
- Sample of STL(Vector):begin and rbegin
- eclipse 第三方jar包 关联源码,提示 The Jar of this class file belongs to container 'Android Dependencies'
- Inversion of Control Containers and the Dependency Injection pattern
- STL中remove_if()/find_if()/replace_if()的使用方法(The usages of remove_if()/find_if()/replace_if() )
- Inversion of Control Containers and the Dependency Injection pattern
- The Standard Librarian: I/O and Function Objects: Containers of Pointers
- 实战c++中的vector系列--vector应用之STL的find、find_if、find_end、find_first_of、find_if_not(C++11)
- Inversion of Control Containers and the Dependency Injection pattern
- [笔记] Week 2: The Psychology of Self-Presentation and Persuasion
- 例题5-2 UVa101 The Blocks Problem(STL:vector)
- UVa 101 The Blocks Problem 【STL】【vector】【模拟】
- 实战c++中的vector系列--vector应用之STL的find、find_if、find_end、find_first_of、find_if_not(C++11)
- The Design and Implementation of Two-dimensional Vector Graphics Interactive Tools Based on “Smart Handle”
- Inversion of Control Containers and the Dependency Injection pattern--Martin Fowler