⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chapter04.html

📁 Thinking in c++ 2nd edition,c++编程思想(第2版)
💻 HTML
📖 第 1 页 / 共 5 页
字号:
<font color=#0000ff>void</font>* fetch(CStash* s, <font color=#0000ff>int</font> index) {
  <font color=#009900>// Check index boundaries:</font>
  assert(0 &lt;= index);
  <font color=#0000ff>if</font>(index &gt;= s-&gt;next)
    <font color=#0000ff>return</font> 0; <font color=#009900>// To indicate the end</font>
  <font color=#009900>// Produce pointer to desired element:</font>
  <font color=#0000ff>return</font> &amp;(s-&gt;storage[index * s-&gt;size]);
}

<font color=#0000ff>int</font> count(CStash* s) {
  <font color=#0000ff>return</font> s-&gt;next;  <font color=#009900>// Elements in CStash</font>
}

<font color=#0000ff>void</font> inflate(CStash* s, <font color=#0000ff>int</font> increase) {
  assert(increase &gt; 0);
  <font color=#0000ff>int</font> newQuantity = s-&gt;quantity + increase;
  <font color=#0000ff>int</font> newBytes = newQuantity * s-&gt;size;
  <font color=#0000ff>int</font> oldBytes = s-&gt;quantity * s-&gt;size;
  <font color=#0000ff>unsigned</font> <font color=#0000ff>char</font>* b = <font color=#0000ff>new</font> <font color=#0000ff>unsigned</font> <font color=#0000ff>char</font>[newBytes];
  <font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i &lt; oldBytes; i++)
    b[i] = s-&gt;storage[i]; <font color=#009900>// Copy old to new</font>
  <font color=#0000ff>delete</font> [](s-&gt;storage); <font color=#009900>// Old storage</font>
  s-&gt;storage = b; <font color=#009900>// Point to new memory</font>
  s-&gt;quantity = newQuantity;
}

<font color=#0000ff>void</font> cleanup(CStash* s) {
  <font color=#0000ff>if</font>(s-&gt;storage != 0) {
   cout &lt;&lt; <font color=#004488>"freeing storage"</font> &lt;&lt; endl;
   <font color=#0000ff>delete</font> []s-&gt;storage;
  }
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><B>initialize(&#160;)</B> performs the
necessary setup for <B>struct CStash</B> by setting the internal variables to
appropriate values. Initially, the <B>storage</B> pointer is set to zero &#8211;
no initial storage is allocated.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>add(&#160;)</B> function inserts
an element into the <B>CStash</B> at the next available location. First, it
checks to see if there is any available space left. If not, it expands the
storage using the <B>inflate(&#160;)</B> function, described
later.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Because the compiler doesn&#8217;t know
the specific type of the variable being stored (all the function gets is a
<B>void*</B>), you can&#8217;t just do an assignment, which would certainly be
the convenient thing. Instead, you must copy the variable byte-by-byte. The most
straightforward way to perform the copying is with array indexing. Typically,
there are already data bytes in <B>storage</B>, and this is indicated by the
value of <B>next</B>. To start with the right byte offset, <B>next</B> is
multiplied by the size of each element (in bytes) to produce <B>startBytes</B>.
Then the argument <B>element</B> is cast to an <B>unsigned char</B>* so that it
can be addressed byte-by-byte and copied into the available <B>storage</B>
space. <B>next</B> is incremented so that it indicates the next available piece
of storage, and the &#8220;index number&#8221; where the value was stored so
that value can be retrieved using this index number with
<B>fetch(&#160;)</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><B>fetch(&#160;) </B>checks to see that
the index isn&#8217;t out of bounds and then returns the address of the desired
variable, calculated using the <B>index</B> argument. Since <B>index
</B>indicates the number of elements to offset into the <B>CStash</B>, it must
be multiplied by the number of bytes occupied by each piece to produce the
numerical offset in bytes. When this offset is used to index into <B>storage
</B>using array indexing, you don&#8217;t get the address, but instead the byte
at the address. To produce the address, you must use the address-of operator
<B>&amp;</B>. </FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><B>count(&#160;)</B> may look a bit
strange at first to a seasoned C programmer. It seems like a lot of trouble to
go through to do something that would probably be a lot easier to do by hand. If
you have a <B>struct CStash</B> called <B>intStash</B>, for example, it would
seem much more straightforward to find out how many elements it has by saying
<B>intStash.next</B> instead of making a function call (which has overhead),
such as <B>count(&amp;intStash)</B>. However, if you wanted to change the
internal representation of <B>CStash</B> and thus the way the count was
calculated, the function call interface allows the necessary flexibility. But
alas, most programmers won&#8217;t bother to find out about your
&#8220;better&#8221; design for the library. They&#8217;ll look at the
<B>struct</B> and grab the <B>next</B> value directly, and possibly even change
<B>next</B> without your permission. If only there were some way for the library
designer to have better control over things like this! (Yes, that&#8217;s
foreshadowing.)</FONT><A NAME="_Toc312373822"></A><A NAME="_Toc472654816"></A><BR></P></DIV>
<A NAME="Heading190"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Dynamic storage
allocation<BR><A NAME="Index977"></A><A NAME="Index978"></A><A NAME="Index979"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You never know the maximum amount of
storage you might need for a <B>CStash</B>, so the memory pointed to by
<B>storage</B> is allocated from the <I>heap</I>. The
heap<A NAME="Index980"></A> is a big block of memory used for allocating smaller
pieces at runtime. You use the heap when you don&#8217;t know the size of the
memory you&#8217;ll need while you&#8217;re writing a program. That is, only at
runtime will you find out that you need space to hold 200 <B>Airplane</B>
variables instead of 20. In Standard C, dynamic-memory allocation functions
include <B>malloc(&#160;)<A NAME="Index981"></A></B>,
<B>calloc(&#160;)<A NAME="Index982"></A></B>,
<B>realloc(&#160;)<A NAME="Index983"></A></B>, and
<B>free(&#160;)<A NAME="Index984"></A></B>. Instead of library calls, however,
C++ has a more sophisticated (albeit simpler to use) approach to dynamic memory
that is integrated into the language via the keywords
<A NAME="Index985"></A><A NAME="Index986"></A><B>new</B> and
<A NAME="Index987"></A><A NAME="Index988"></A><B>delete</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>inflate(&#160;)</B> function uses
<B>new</B> to get a bigger chunk of space for the <B>CStash</B>. In this
situation, we will only expand memory and not shrink it, and the
<A NAME="Index989"></A><B>assert(&#160;)</B> will guarantee that a negative
number is not passed to <B>inflate(&#160;)</B> as the <B>increase</B> value. The
new number of elements that can be held (after <B>inflate(&#160;)</B> completes)
is calculated as <B>newQuantity</B>, and this is multiplied by the number of
bytes per element to produce <B>newBytes</B>, which will be the number of bytes
in the allocation. So that we know how many bytes to copy over from the old
location, <B>oldBytes</B> is calculated using the old
<B>quantity</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The actual storage allocation occurs in
the <A NAME="Index990"></A><I>new-expression</I>, which is the expression
involving the <B>new</B> keyword:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>new</font> <font color=#0000ff>unsigned</font> <font color=#0000ff>char</font>[newBytes];</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The general form of the new-expression
is:</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><B>new Type;</B></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">in which <B>Type</B> describes the type
of variable you want allocated on the heap. In this case, we want an array of
<B>unsigned char</B> that is <B>newBytes</B> long, so that is what appears as
the <B>Type</B>. You can also allocate something as simple as an <B>int</B> by
saying:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>new</font> <font color=#0000ff>int</font>;</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">and although this is rarely done, you can
see that the form is consistent.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">A new-expression returns a <I>pointer</I>
to an object of the exact type that you asked for. So if you say <B>new
Type</B>, you get back a pointer to a <B>Type</B>.<B> </B>If you say <B>new
int</B>, you get back a pointer to an <B>int</B>. If you want a <B>new</B>
<B>unsigned char</B> array, you get back a pointer to the first element of that
array. The compiler will ensure that you assign the return value of the
new-expression to a pointer of the correct type.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Of course, any time you request memory
it&#8217;s possible for the request to fail, if there is no more memory. As you
will learn, C++ has mechanisms that come into play if the memory-allocation
operation is unsuccessful.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Once the new storage is allocated, the
data in the old storage must be copied to the new storage; this is again
accomplished with array indexing, copying one byte at a time in a loop. After
the data is copied, the old storage must be released so that it can be used by
other parts of the program if they need new storage. The <B>delete</B> keyword
is the complement of <B>new</B>, and must be applied to release any storage that
is allocated with <B>new</B> (if you forget to use <B>delete</B>, that storage
remains unavailable, and if this so-called
<A NAME="Index991"></A><A NAME="Index992"></A><I>memory leak </I>happens enough,
you&#8217;ll run out of memory). In addition, there&#8217;s a special syntax
when you&#8217;re deleting an array. It&#8217;s as if you must remind the
compiler that this pointer is not just pointing to one object, but to an array
of objects: you put a set of empty square brackets in front of the pointer to be
deleted:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>delete</font> []myArray;</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Once the old storage has been deleted,
the pointer to the new storage can be assigned to the <B>storage</B> pointer,
the quantity is adjusted, and <B>inflate(&#160;)</B> has completed its
job.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Note that the heap manager is fairly
primitive. It gives you chunks of memory and takes them back when you
<B>delete</B> them. There&#8217;s no inherent facility for
<A NAME="Index993"></A><I>heap compaction</I>, which compresses the heap to
provide bigger free chunks. If a program allocates and frees heap storage for a
while, you can end up with a
<A NAME="Index994"></A><A NAME="Index995"></A><I>fragmented</I> heap that has
lots of memory free, but without any pieces that are big enough to allocate the
size you&#8217;re looking for at the moment. A <A NAME="Index996"></A>heap
compactor complicates a program because it moves memory chunks around, so your
pointers won&#8217;t retain their proper values. Some operating environments
have heap compaction built in, but they require you to use special memory
<I>handles</I> (which can be temporarily converted to pointers, after locking
the memory so the heap compactor can&#8217;t move it) instead of pointers. You
can also build your own heap-compaction scheme, but this is not a task to be
undertaken lightly.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When you create a
<A NAME="Index997"></A><A NAME="Index998"></A>variable on the stack at
compile-time, the storage for that variable is automatically created and freed
by the compiler. The compiler knows exactly how much storage is needed, and it
knows the lifetime of the variables because of scoping. With dynamic memory
allocation, however, the compiler doesn&#8217;t know how much storage
you&#8217;re going to need, <I>and</I> it doesn&#8217;t know the lifetime of
that storage. That is, the storage doesn&#8217;t get cleaned up automatically.
Therefore, you&#8217;re responsible for releasing the storage using
<B>delete</B>, which tells the heap manager that storage can be used by the next
call to <B>new</B>. The logical place for this to happen in the library is in
the <B>cleanup(&#160;)</B> function because that is where all the closing-up
housekeeping is done.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To test the library, two <B>CStash</B>es
are created. The first holds <B>int</B>s and the second holds arrays of 80
<B>char</B>s:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C04:CLibTest.cpp</font>
<font color=#009900>//{L} CLib</font>
<font color=#009900>// Test the C-like library</font>
#include <font color=#004488>"CLib.h"</font>
#include &lt;fstream&gt;
#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;cassert&gt;
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -