📄 chap06.htm
字号:
function.</FONT><A NAME="fnB15" HREF="#fn15">[15]</A><FONT FACE="Georgia">
However, a function template is useful in all sorts of places, as demonstrated
in the first example that follows. The second example shows a function template
used with containers and iterators.
</FONT><A NAME="_Toc312374090"></A><A NAME="_Toc519041981"></A><BR></P></DIV>
<A NAME="Heading158"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H3 ALIGN="LEFT">
A string conversion system</H3></FONT>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C06:stringConv.h</font>
<font color=#009900>// Chuck Allison's string converter</font>
#ifndef STRINGCONV_H
#define STRINGCONV_H
#include <string>
#include <sstream>
<font color=#0000ff>template</font><<font color=#0000ff>typename</font> T>
T fromString(<font color=#0000ff>const</font> std::string& s) {
std::istringstream is(s);
T t;
is >> t;
<font color=#0000ff>return</font> t;
}
<font color=#0000ff>template</font><<font color=#0000ff>typename</font> T>
std::string toString(<font color=#0000ff>const</font> T& t) {
std::ostringstream s;
s << t;
<font color=#0000ff>return</font> s.str();
}
#endif <font color=#009900>// STRINGCONV_H ///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here’s a test program, that
includes the use of the Standard Library <B>complex</B> number
type:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C06:stringConvTest.cpp</font>
<font color=#009900>//{L} ../TestSuite/Test</font>
<font color=#009900>//{-bor} Core dumps on execution</font>
<font color=#009900>//{-msc} Core dumps on execution</font>
#include <font color=#004488>"stringConv.h"</font>
#include <iostream>
#include <complex>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>int</font> main() {
<font color=#0000ff>int</font> i = 1234;
cout << <font color=#004488>"i == \"</font><font color=#004488>" << toString(i) << "</font>\<font color=#004488>"\n"</font>;
<font color=#0000ff>float</font> x = 567.89;
cout << <font color=#004488>"x == \"</font><font color=#004488>" << toString(x) << "</font>\<font color=#004488>"\n"</font>;
complex<<font color=#0000ff>float</font>> c(1.0, 2.0);
cout << <font color=#004488>"c == \"</font><font color=#004488>" << toString(c) << "</font>\<font color=#004488>"\n"</font>;
cout << endl;
i = fromString<<font color=#0000ff>int</font>>(string(<font color=#004488>"1234"</font>));
cout << <font color=#004488>"i == "</font> << i << endl;
x = fromString<<font color=#0000ff>float</font>>(string(<font color=#004488>"567.89"</font>));
cout << <font color=#004488>"x == "</font> << x << endl;
c = fromString< complex<<font color=#0000ff>float</font>> >(string(<font color=#004488>"(1.0,2.0)"</font>));
cout << <font color=#004488>"c == "</font> << c << endl;
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The output is what you’d
expect:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>i == <font color=#004488>"1234"</font>
x == <font color=#004488>"567.89"</font>
c == <font color=#004488>"(1,2)"</font>
i == 1234
x == 567.89
c == (1,2)</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><A NAME="_Toc519041982"></A><BR></P></DIV>
<A NAME="Heading159"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H3 ALIGN="LEFT">
A memory allocation system<BR><A NAME="Index468"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">There are a few things you can do to make
the raw memory allocation routines <B>malloc( )<A NAME="Index469"></A></B>,
<B>calloc( )<A NAME="Index470"></A></B> and
<B>realloc( )<A NAME="Index471"></A></B> safer. The following function
template produces one function <B>getmem( )</B> that either allocates a new
piece of memory or resizes an existing piece (like <B>realloc( )</B>). In
addition, it zeroes only the new memory, and it checks to see that the memory is
successfully allocated. Also, you only tell it the number of elements of the
type you want, not the number of bytes, so the possibility of a programmer error
is reduced. Here’s the header file:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C06:Getmem.h</font>
<font color=#009900>// Function template for memory</font>
#ifndef GETMEM_H
#define GETMEM_H
#include <font color=#004488>"..</font><font color=#004488>/require.h"</font>
#include <cstdlib>
#include <cstring>
<font color=#0000ff>template</font><<font color=#0000ff>class</font> T>
<font color=#0000ff>void</font> getmem(T*& oldmem, <font color=#0000ff>int</font> elems) {
<font color=#0000ff>typedef</font> <font color=#0000ff>int</font> cntr; <font color=#009900>// Type of element counter</font>
<font color=#0000ff>const</font> <font color=#0000ff>int</font> csz = <font color=#0000ff>sizeof</font>(cntr); <font color=#009900>// And size</font>
<font color=#0000ff>const</font> <font color=#0000ff>int</font> tsz = <font color=#0000ff>sizeof</font>(T);
<font color=#0000ff>if</font>(elems == 0) {
free(&(((cntr*)oldmem)[-1]));
<font color=#0000ff>return</font>;
}
T* p = oldmem;
cntr oldcount = 0;
<font color=#0000ff>if</font>(p) { <font color=#009900>// Previously allocated memory</font>
<font color=#009900>// Old style:</font>
<font color=#009900>// ((cntr*)p)--; // Back up by one cntr</font>
<font color=#009900>// New style:</font>
cntr* tmp = <font color=#0000ff>reinterpret_cast</font><cntr*>(p);
p = <font color=#0000ff>reinterpret_cast</font><T*>(--tmp);
oldcount = *(cntr*)p; <font color=#009900>// Previous # elems</font>
}
T* m = (T*)realloc(p, elems * tsz + csz);
require(m != 0);
*((cntr*)m) = elems; <font color=#009900>// Keep track of count</font>
<font color=#0000ff>const</font> cntr increment = elems - oldcount;
<font color=#0000ff>if</font>(increment > 0) {
<font color=#009900>// Starting address of data:</font>
<font color=#0000ff>long</font> startadr = (<font color=#0000ff>long</font>)&(m[oldcount]);
startadr += csz;
<font color=#009900>// Zero the additional new memory:</font>
memset((<font color=#0000ff>void</font>*)startadr, 0, increment * tsz);
}
<font color=#009900>// Return the address beyond the count:</font>
oldmem = (T*)&(((cntr*)m)[1]);
}
<font color=#0000ff>template</font><<font color=#0000ff>class</font> T>
<font color=#0000ff>inline</font> <font color=#0000ff>void</font> freemem(T * m) { getmem(m, 0); }
#endif <font color=#009900>// GETMEM_H ///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To be able to zero only the new memory, a
counter indicating the number of elements allocated is attached to the beginning
of each block of memory. The <B>typedef cntr</B> is the type of this counter; it
allows you to change from <B>int</B> to <B>long</B> if you need to handle larger
chunks (other issues come up when using <B>long</B>, however – these are
seen in compiler warnings).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">A pointer reference is used for the
argument <B>oldmem</B> because the outside variable (a pointer) must be changed
to point to the new block of memory. <B>oldmem</B> must point to zero (to
allocate new memory) or to an existing block of memory <I>that was created with
</I><B>getmem( )</B>. This function assumes you’re using it properly,
but for debugging you could add an additional tag next to the counter containing
an identifier, and check that identifier in <B>getmem( )</B> to help
discover incorrect calls.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">If the number of elements requested is
zero, the storage is freed. There’s an additional function template
<B>freemem( )</B> that aliases this behavior.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You’ll notice that
<B>getmem( )</B> is very low-level – there are lots of casts and byte
manipulations. For example, the <B>oldmem</B> pointer doesn’t point to the
true beginning of the memory block, but just <I>past</I> the beginning to allow
for the counter. So to <B>free( )</B> the memory block,
<B>getmem( )</B> must back up the pointer by the amount of space occupied
by <B>cntr</B>. Because <B>oldmem</B> is a <B>T*</B>, it must first be cast to a
<B>cntr*</B>, then indexed backwards one place. Finally the address of that
location is produced for <B>free( )</B> in the expression:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>free(&(((cntr*)oldmem)[-1]));</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Similarly, if this is previously
allocated memory, <B>getmem( )</B> must back up by one <B>cntr</B> size to
get the true starting address of the memory, and then extract the previous
number of elements. The true starting address is required inside
<B>realloc( )</B>. If the storage size is being increased, then the
difference between the new number of elements and the old number is used to
calculate the starting address and the amount of memory to zero in
<B>memset( )</B>. Finally, the address beyond the count is produced to
assign to <B>oldmem</B> in the statement:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>oldmem = (T*)&(((cntr*)m)[1]);</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Again, because <B>oldmem</B> is a
reference to a pointer, this has the effect of changing the outside argument
passed to <B>getmem( )</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here’s a program to test
<B>getmem( )</B>. It allocates storage and fills it up with values, then
increases that amount of storage:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C06:Getmem.cpp</font>
<font color=#009900>// Test memory function template</font>
<font color=#009900>//{L} ../TestSuite/Test</font>
#include <font color=#004488>"Getmem.h"</font>
#include <iostream>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>int</font> main() {
<font color=#0000ff>int</font>* p = 0;
getmem(p, 10);
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < 10; i++) {
cout << p[i] << ' ';
p[i] = i;
}
cout << '\n';
getmem(p, 20);
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> j = 0; j < 20; j++) {
cout << p[j] << ' ';
p[j] = j;
}
cout << '\n';
getmem(p, 25);
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> k = 0; k < 25; k++)
cout << p[k] << ' ';
freemem(p);
cout << '\n';
<font color=#0000ff>float</font>* f = 0;
getmem(f, 3);
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> u = 0; u < 3; u++) {
cout << f[u] << ' ';
f[u] = u + 3.14159;
}
cout << '\n';
getmem(f, 6);
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> v = 0; v < 6; v++)
cout << f[v] << ' ';
freemem(f);
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">After each <B>getmem( )</B>, the
values in memory are printed out to show that the new ones have been zeroed.
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Notice that a different version of
<B>getmem( )</B> is instantiated for the <B>int</B> and <B>float</B>
pointers. You might think that because all the manipulations are so low-level
you could get away with a single non-template function and pass a
<B>void*&</B> as <B>oldmem</B>. This doesn’t work because then the
compiler must do a conversion from your type to a <B>void*</B>. To take the
reference, it makes a temporary. This produces an error because then
you’re modifying the temporary pointer, not the pointer you want to
change. So the function template is necessary to produce the exact type for the
argument.</FONT><A NAME="_Toc519041983"></A><BR></P></DIV>
<A NAME="Heading160"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
Type induction in function templates </H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">As a simple but very useful example,
consider the following:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: :arraySize.h</font>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -