📄 index.html
字号:
</pre>
<p>On most implementations, the parameterized types <tt>size_type</tt> and <tt>difference_type</tt>
have the default values <tt>size_t</tt> and <tt>ptrdiff_t</tt>, respectively.
However, they can be replaced by other types for particular specializations.</p>
<p>The storage of STL containers automatically grows as necessary, freeing the
programmer from this tedious and error-prone task. For example, a <tt>vector</tt>
can be used to read an unknown number of elements from the keyboard:</p>
<pre>
<tt>#include <vector></tt>
<tt>#include <iostream></tt>
<tt>using namespace std;</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt> vector <int> vi;</tt>
<tt> for (;;) //read numbers from a user's console until 0 is input</tt>
<tt> {</tt>
<tt> int temp;</tt>
<tt> cout<<"enter a number; press 0 to terminate" <<endl;</tt>
<tt> cin>>temp;</tt>
<tt> if (temp == 0 ) break; //exit from loop?</tt>
<tt> vi.push_back(temp); //insert int into the buffer</tt>
<tt> }</tt>
<tt> cout<< "you entered "<< vi.size() <<" elements" <<endl;</tt>
<tt> return 0;</tt>
<tt>}//end main</tt>
</pre>
<h3> <a name="Heading13">Container Reallocation</a></h3>
<p>The memory allocation scheme of STL containers must address two conflicting
demands. On the one hand, a container should not preallocate large amounts of
memory because it can impair the system's performance. On the other hand, it
is inefficient to allow a container reallocate memory whenever it stores a few
more elements. The allocation strategy has to walk a thin line. On many implementations,
a container initially allocates a small memory buffer, which grows exponentially
with every reallocation. Sometimes, however, it is possible to estimate in advance
how many elements the container will have to store. In this case, the user can
preallocate a sufficient amount of memory in advance so that the recurrent reallocation
process can be avoided. Imagine a mail server of some Internet Service Provider:
The server is almost idle at 4 a.m. At 9 a.m., however, it has to transfer thousands
of emails every minute. The incoming emails are first stored in a <tt>vector</tt>
before they are routed to other mail servers across the Web. Allowing the container
to reallocate itself little by little with every few dozen emails can degrade
performance.</p>
<h4> What Happens During Reallocation?</h4>
<p>The reallocation process consists of four steps. First, a new memory buffer
that is large enough to store the container is allocated. Second, the existing
elements are copied to the new memory location. Third, the destructors of the
elements in their previous location are successively invoked. Finally, the original
memory buffer is released. Obviously, reallocation is a costly operation. You
can avert reallocation by calling the member function <tt>reserve()</tt>. <tt>reserve(</tt><cite>n</cite><tt>)</tt>
ensures that the container reserves sufficient memory for at least <cite>n</cite>
elements in advance, as in the following example:</p>
<pre>
<tt>class Message { /*...*/};</tt>
<tt>#include <vector></tt>
<tt>using namespace std;</tt>
<tt>int FillWithMessages(vector<Message>& msg_que); //severe time constraints</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt> vector <Message> msgs;</tt>
<tt> // before entering a time-critical section, make room for 1000 Messages</tt>
<tt> msgs.reserve(1000);</tt>
<tt>//no re-allocation should occur before 1000 objects have been stored in vector</tt>
<tt> FillWithMessages(msgs);</tt>
<tt> return 0;</tt>
<tt>}</tt>
</pre>
<h3> <a name="Heading14">capacity() and size()</a></h3>
<p><tt>capacity()</tt> returns the total number of elements that the container
can hold without requiring reallocation. <tt>size()</tt> returns the number
of elements that are currently stored in the container. In other words, <tt>capacity()
- size()</tt> is the number of available "free slots" that can be filled with
additional elements without reallocating. The capacity of a container can be
resized explicitly by calling either <tt>reserve()</tt> or <tt>resize()</tt>.
These member functions differ in two respects. <tt>resize(</tt><cite>n</cite><tt>)</tt>
allocates memory for <cite>n</cite> objects and default-initializes them (you
can provide a different initializer value as the second optional argument).
</p>
<p><tt>reserve()</tt> allocates raw memory without initializing it. In addition,
<tt>reserve()</tt> does not change the value that is returned from <tt>size()</tt>
it only changes the value that is returned from <tt>capacity</tt>(). <tt>resize()</tt>
changes both these values. For example</p>
<pre>
<tt>#include <iostream></tt>
<tt>#include <vector></tt>
<tt>#include <string></tt>
<tt>using namespace std;</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt> vector <string> vs;</tt>
<tt> vs.reserve(10); //make room for at least 10 more strings</tt>
<tt> vs.push_back(string()); //insert an element </tt>
<tt> cout<<"size: "<< vs.size()<<endl; //output: 1</tt>
<tt> cout<<"capacity: "<<vs.capacity()<<endl; //output: 10</tt>
<tt> cout<<"there's room for "<<vs.capacity() - vs.size()</tt>
<tt> <<" elements before reallocation"<<endl;</tt>
<tt> //allocate 10 more elements, initialized each with string::string()</tt>
<tt> vs.resize(20);</tt>
<tt> cout<<"size: "<< vs.size()<<endl; //output 20</tt>
<tt> cout<<"capacity: "<<vs.capacity()<<endl; //output 20;</tt>
<tt> return 0;</tt>
<tt>}</tt>
</pre>
<h3> <a name="Heading15">Specifying the Container's Capacity During Construction</a></h3>
<p>Up until now, the examples in this chapter have used explicit operations to
preallocate storage by calling either <tt>reserve()</tt> or <tt>resize()</tt>.
However, it is possible to specify the requested storage size during construction.
For example</p>
<pre>
<tt>#include <vector></tt>
<tt>using namespace std;</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt> vector<int> vi(1000); //initial storage for 1000 int's</tt>
<tt> //vi contains 1000 elements initialized by int::int()</tt>
<tt> return 0;</tt>
<tt>}</tt>
</pre>
<p>Remember that <tt>reserve()</tt> allocates raw memory without initializing
it. The constructor, on the other hand, initializes the allocated elements by
invoking their default constructor. It is possible to specify a different initializer
value, though:</p>
<pre>
<tt>vector<int> vi(1000, 4); //initial all 1000 int's with 4</tt>
</pre>
<h3> <a name="Heading16">Accessing a Single Element</a></h3>
<p>The overloaded operator <tt>[]</tt> and the member function <tt>at()</tt> enable
direct access to a vector's element. Both have a <tt>const</tt> and a non-<tt>const</tt>
version, so they can be used to access an element of a <tt>const</tt> and a
non-<tt>const</tt> vector, respectively.</p>
<p>The overloaded <tt>[]</tt> operator was designed to be as efficient as its
built-in counterpart. Therefore, <tt>[]</tt> does not check to see if its argument
actually refers to a valid element. The lack of runtime checks ensures the fastest
access time (an operator <tt>[]</tt> call is usually inlined). However, using
operator <tt>[]</tt> with an illegal subscript yields undefined behavior. When
performance is paramount, and when the code is written carefully so that only
legal subscripts are accessed, use the <tt>[]</tt> operator. The <tt>[]</tt>
notation is also more readable and intuitive. Nonetheless, runtime checks are
unavoidable in some circumstances for instance, when the subscript value is
received from an external source such as a function, a database record, or a
human operator. In such cases you should use the member function <tt>at()</tt>
instead of operator <tt>[]</tt>. <tt>at()</tt> performs range checking and,
in case of an attempt to access an out of range member, it throws an exception
of type <tt>std::out_of_range</tt>. Here is an example:</p>
<pre>
<tt>#include <vector></tt>
<tt>#include <iostream></tt>
<tt>#include <string></tt>
<tt>#include <stdexcept></tt>
<tt>using namespace std;</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt> vector<string> vs; // vs has no elements currently</tt>
<tt> vs.push_back("string"); //add first element</tt>
<tt> vs[0] = "overriding string"; //override it using []</tt>
<tt> try</tt>
<tt> {</tt>
<tt> cout<< vs.at(10) <<endl; //out of range element, exception thrown</tt>
<tt> }</tt>
<tt> catch(std::out_of_range & except)</tt>
<tt> {</tt>
<tt> // handle out-of-range subscript</tt>
<tt> }</tt>
<tt>}//end main</tt>
</pre>
<h3> <a name="Heading17">Front and Back Operations</a></h3>
<p>Front and back operations refer to the beginning and the end of a container,
respectively. The member function <tt>push_back()</tt> appends a single element
to the end of the container. When the container has exhausted its free storage,
it reallocates additional storage, and then appends the element. The member
function <tt>pop_back()</tt> removes the last element from the container. The
member functions <tt>front()</tt> and <tt>back()</tt> access a single element
at the container's beginning and end, respectively. <tt>front()</tt> and <tt>back()</tt>
both have a <tt>const</tt> and a non-<tt>const</tt> version. For example</p>
<pre>
<tt>#include <iostream></tt>
<tt>#include <vector></tt>
<tt>using namespace std;</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt> vector <short> v;</tt>
<tt> v.push_back(5);</tt>
<tt> v.push_back(10);</tt>
<tt> cout<<"front: " << v.front() << endl; //5</tt>
<tt> cout<<"back: " << v.back() << endl; //10</tt>
<tt> v.pop_back(); //remove v[1]</tt>
<tt> cout<<"back: " << v.back() << endl; //now 5</tt>
<tt> return 0;</tt>
<tt>}</tt>
</pre>
<h3> <a name="Heading18">Container Assignment</a></h3>
<p>STL containers overload the assignment operator, thereby allowing containers
of the same type to be assigned easily. For example</p>
<pre>
<tt>#include <iostream></tt>
<tt>#include<vector></tt>
<tt>using namespace std;</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt> vector <int> vi;</tt>
<tt> vi.push_back(1);</tt>
<tt> vi.push_back(2);</tt>
<tt> vector <int> new_vector;</tt>
<tt> //copy the contents of vi to new_vector, which automatically grows as needed</tt>
<tt> new_vector = vi;</tt>
<tt> cout << new_vector[0] << new_vector[1] << endl; // display 1 and 2</tt>
<tt> return 0; </tt>
<tt>} </tt>
</pre>
<h3> <a name="Heading19">Contiguity of Vectors</a></h3>
<p>Built-in arrays in C++ reside in contiguous chunks of memory. The Standard,
however, does not require that vector elements occupy contiguous memory. When
STL was added to the Standard, it seemed intuitive that vectors should store
their elements contiguously, so contiguity never became an explicit requirement.
Indeed, all current STL implementations follow this convention. The current
specification, however, permits implementations that do not use contiguous memory.
This loophole will probably be fixed by the Standardization committee in the
future, and vector contiguity will become a Standard requirement.</p>
<h3> <a name="Heading20">A vector<Base> Should not Store Derived Objects</a></h3>
<p>Each element in a vector must have the same size. Because a derived object
can have additional members, its size might be larger than the size of its base
class. Avoid storing a derived object in a <tt>vector<Base></tt> because
it can cause object slicing with undefined results. You can, however, achieve
the desired polymorphic behavior by storing a pointer to a derived object in
a <tt>vector<Base*>.</tt></p>
<h3> <a name="Heading21">FIFO Data Models</a></h3>
<p>In a queue data model (a queue is also called FIFO first in first out), the
first element that is inserted is located at the topmost position, and any subsequent
elements are located at lower positions. The two basic operations in a queue
are <tt>pop()</tt> and <tt>push()</tt>. A <tt>push()</tt> operation inserts
an element into the bottom of the queue. A <tt>pop()</tt> operation removes
the element at the topmost position, which was the first to be inserted; consequently,
the element that is located one position lower becomes the topmost element.
The STL <tt>queue</tt> container can be used as follows:</p>
<pre>
<tt>#include <iostream></tt>
<tt>#include <queue></tt>
<tt>using namespace std;</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt> queue <int> iq;</tt>
<tt> iq.push(93); //insert the first element, it is the top-most one</tt>
<tt> iq.push(250);</tt>
<tt> iq.push(10); //last element inserted is located at the bottom</tt>
<tt> cout<<"currently there are "<< iq.size() << " elements" << endl;</tt>
<tt> while (!iq.empty() )</tt>
<tt> {</tt>
<tt> cout <<"the last element is: "<< iq.front() << endl; //front() returns </tt>
<tt> //the top-most element</tt>
<tt> iq.pop(); //remove the top-most element</tt>
<tt> }</tt>
<tt> return 0;</tt>
<tt>}</tt>
</pre>
<p>STL also defines a double-ended queue, or <i>deque</i> (pronounced "deck")
container. A <tt>deque</tt> is a queue that is optimized to support operations
at both ends efficiently. Another type of queue is a <cite>priority_queue</cite>.
A <tt>priority_queue</tt> has all its elements internally sorted according to
their priority. The element with the highest priority is located at the top.
To qualify as an element of <tt>priority_queue</tt>, an object has to define
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -