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

📄 chapter16.html

📁 《C++编程思想》中文版。。。。。。。。。。。。。
💻 HTML
📖 第 1 页 / 共 5 页
字号:
name must be accompanied by its template argument list<A NAME="Index2634"></A>,
as in <B>Array&lt;T&gt;::operator[]</B>. You can imagine that internally, the
class name is being decorated with the arguments in the template argument list
to produce a unique class name identifier for each template
instantiation.</FONT><BR></P></DIV>
<A NAME="Heading471"></A><FONT FACE = "Verdana"><H4 ALIGN="LEFT">
Header files<BR><A NAME="Index2635"></A><A NAME="Index2636"></A></H4></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Even if you create non-inline function
definitions, you&#8217;ll usually want to put all declarations <I>and</I>
definitions for a template into a header file. This may seem to violate the
normal header file rule of &#8220;Don&#8217;t put in anything that allocates
storage,&#8221; (which prevents multiple definition errors at link time), but
template definitions are special. Anything preceded by
<B>template&lt;...&gt;</B> means the compiler won&#8217;t allocate storage for
it at that point, but will instead wait until it&#8217;s told to (by a template
instantiation), and that somewhere in the compiler and linker there&#8217;s a
mechanism for removing multiple definitions<A NAME="Index2637"></A> of an
identical template. So you&#8217;ll almost always put the entire template
declaration <I>and</I> definition in the header file, for ease of
use.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">There are times when you may need to
place the template definitions in a separate <B>cpp</B> file to satisfy special
needs (for example, forcing template instantiations to exist in only a single
Windows <B>dll</B> file). Most compilers have some mechanism to allow this;
you&#8217;ll have to investigate your particular compiler&#8217;s documentation
to use it.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Some people feel that putting all of the
source code for your implementation in a header file makes it possible for
people to steal and modify your code if they buy a library from you. This might
be an issue, but it probably depends on the way you look at the problem: Are
they buying a product or a service? If it&#8217;s a product, then you have to do
everything you can to protect it, and probably you don&#8217;t want to give
source code, just compiled code. But many people see software as a service, and
even more than that, a subscription service. The customer wants your expertise,
they want you to continue maintaining this piece of reusable code so that they
don&#8217;t have to &#8211; so they can focus on getting <I>their</I> job done.
I personally think most customers will treat you as a valuable resource and will
not want to jeopardize their relationship with you. As for the few who want to
steal rather than buy or do original work, they probably can&#8217;t keep up
with you
anyway.</FONT><A NAME="_Toc312374071"></A><A NAME="_Toc472655053"></A><BR></P></DIV>
<A NAME="Heading472"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
IntStack as a template</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here is the container and iterator from
<B>IntStack.cpp</B>, implemented as a generic container class using
templates:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C16:StackTemplate.h</font>
<font color=#009900>// Simple stack template</font>
#ifndef STACKTEMPLATE_H
#define STACKTEMPLATE_H
#include <font color=#004488>"../require.h"</font>

<font color=#0000ff>template</font>&lt;<font color=#0000ff>class</font> T&gt;
<font color=#0000ff>class</font> StackTemplate {
  <font color=#0000ff>enum</font> { ssize = 100 };
  T stack[ssize];
  <font color=#0000ff>int</font> top;
<font color=#0000ff>public</font>:
  StackTemplate() : top(0) {}
  <font color=#0000ff>void</font> push(<font color=#0000ff>const</font> T&amp; i) {
    require(top &lt; ssize, <font color=#004488>"Too many push()es"</font>);
    stack[top++] = i;
  }
  T pop() {
    require(top &gt; 0, <font color=#004488>"Too many pop()s"</font>);
    <font color=#0000ff>return</font> stack[--top];
  }
  <font color=#0000ff>int</font> size() { <font color=#0000ff>return</font> top; }
};
#endif <font color=#009900>// STACKTEMPLATE_H ///:~</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><A NAME="Index2638"></A><A NAME="Index2639"></A><FONT FACE="Georgia">Notice
that a template makes certain assumptions about the objects it is holding. For
example, <B>StackTemplate</B> assumes there is some sort of assignment operation
for <B>T</B> inside the <B>push(&#160;)</B> function. You could say that a
template &#8220;implies an interface&#8221; for the types it is capable of
holding.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><A NAME="Index2640"></A><A NAME="Index2641"></A><A NAME="Index2642"></A><FONT FACE="Georgia">Another
way to say this is that templates provide a kind of <I>weak typing</I> mechanism
for C++, which is ordinarily a strongly-typed language. Instead of insisting
that an object be of some exact type in order to be acceptable, weak typing
requires only that the member functions that it wants to call are
<I>available</I> for a particular object. Thus, weakly-typed code can be applied
to any object that can accept those member function calls, and is thus much more
flexible</FONT><A NAME="fnB63" HREF="#fn63">[63]</A><A NAME="Index2643"></A><A NAME="Index2644"></A><A NAME="Index2645"></A><A NAME="Index2646"></A><FONT FACE="Georgia">.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here&#8217;s the revised example to test
the template:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C16:StackTemplateTest.cpp</font>
<font color=#009900>// Test simple stack template</font>
<font color=#009900>//{L} fibonacci</font>
#include <font color=#004488>"fibonacci.h"</font>
#include <font color=#004488>"StackTemplate.h"</font>
#include &lt;iostream&gt;
#include &lt;fstream&gt;
#include &lt;string&gt;
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;

<font color=#0000ff>int</font> main() {
  StackTemplate&lt;<font color=#0000ff>int</font>&gt; is;
  <font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i &lt; 20; i++)
    is.push(fibonacci(i));
  <font color=#0000ff>for</font>(<font color=#0000ff>int</font> k = 0; k &lt; 20; k++)
    cout &lt;&lt; is.pop() &lt;&lt; endl;
  ifstream in(<font color=#004488>"StackTemplateTest.cpp"</font>);
  assure(in, <font color=#004488>"StackTemplateTest.cpp"</font>);
  string line;
  StackTemplate&lt;string&gt; strings;
  <font color=#0000ff>while</font>(getline(in, line))
    strings.push(line);
  <font color=#0000ff>while</font>(strings.size() &gt; 0)
    cout &lt;&lt; strings.pop() &lt;&lt; endl;
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The only difference is in the creation of
<B>is</B>. Inside the template argument list you specify the type of object the
stack and iterator should hold. To demonstrate the genericness of the template,
a <B>StackTemplate</B> is also created to hold <B>string</B>. This is tested by
reading in lines from the source-code
file.</FONT><A NAME="_Toc312374072"></A><A NAME="_Toc472655054"></A><BR></P></DIV>
<A NAME="Heading473"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Constants in
templates<BR><A NAME="Index2647"></A><A NAME="Index2648"></A><A NAME="Index2649"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Template arguments are not restricted to
class types; you can also use built-in types. The values of these arguments then
become compile-time constants for that particular instantiation of the template.
You can even use default values for these arguments. The following example
allows you to set the size of the <B>Array</B> class during instantiation, but
also provides a default value:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C16:Array3.cpp</font>
<font color=#009900>// Built-in types as template arguments</font>
#include <font color=#004488>"../require.h"</font>
#include &lt;iostream&gt;
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;

<font color=#0000ff>template</font>&lt;<font color=#0000ff>class</font> T, <font color=#0000ff>int</font> size = 100&gt;
<font color=#0000ff>class</font> Array {
  T array[size];
<font color=#0000ff>public</font>:
  T&amp; <font color=#0000ff>operator</font>[](<font color=#0000ff>int</font> index) {
    require(index &gt;= 0 &amp;&amp; index &lt; size,
      <font color=#004488>"Index out of range"</font>);
    <font color=#0000ff>return</font> array[index];
  }
  <font color=#0000ff>int</font> length() <font color=#0000ff>const</font> { <font color=#0000ff>return</font> size; }
};

<font color=#0000ff>class</font> Number {
  <font color=#0000ff>float</font> f;
<font color=#0000ff>public</font>:
  Number(<font color=#0000ff>float</font> ff = 0.0f) : f(ff) {}
  Number&amp; <font color=#0000ff>operator</font>=(<font color=#0000ff>const</font> Number&amp; n) {
    f = n.f;
    <font color=#0000ff>return</font> *<font color=#0000ff>this</font>;
  }
  <font color=#0000ff>operator</font> <font color=#0000ff>float</font>() <font color=#0000ff>const</font> { <font color=#0000ff>return</font> f; }
  <font color=#0000ff>friend</font> ostream&amp;
    <font color=#0000ff>operator</font>&lt;&lt;(ostream&amp; os, <font color=#0000ff>const</font> Number&amp; x) {
      <font color=#0000ff>return</font> os &lt;&lt; x.f;
  }
};

<font color=#0000ff>template</font>&lt;<font color=#0000ff>class</font> T, <font color=#0000ff>int</font> size = 20&gt;
<font color=#0000ff>class</font> Holder {
  Array&lt;T, size&gt;* np;
<font color=#0000ff>public</font>:
  Holder() : np(0) {}
  T&amp; <font color=#0000ff>operator</font>[](<font color=#0000ff>int</font> i) {
    require(0 &lt;= i &amp;&amp; i &lt; size);
    <font color=#0000ff>if</font>(!np) np = <font color=#0000ff>new</font> Array&lt;T, size&gt;;
    <font color=#0000ff>return</font> np-&gt;<font color=#0000ff>operator</font>[](i);
  }
  <font color=#0000ff>int</font> length() <font color=#0000ff>const</font> { <font color=#0000ff>return</font> size; }
  ~Holder() { <font color=#0000ff>delete</font> np; }
};

<font color=#0000ff>int</font> main() {
  Holder&lt;Number&gt; h;
  <font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i &lt; 20; i++)
    h[i] = i;
  <font color=#0000ff>for</font>(<font color=#0000ff>int</font> j = 0; j &lt; 20; j++)
    cout &lt;&lt; h[j] &lt;&lt; endl;
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">As before, <B>Array</B> is a checked
array of objects and prevents you from indexing out of bounds. The class
<B>Holder</B> is much like <B>Array</B> except that it has a pointer to an
<B>Array</B> instead of an embedded object of type <B>Array</B>. This pointer is
not initialized in the constructor; the initialization is delayed until the
first access. This is called
<A NAME="Index2650"></A><A NAME="Index2651"></A><I>lazy initialization</I>; you
might use a technique like this if you are creating a lot of objects, but not
accessing them all, and want to save storage.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You&#8217;ll notice that the <B>size</B>
value in both templates is never stored internally in the class, but it is used
as if it were a data member inside the member
functions.</FONT><A NAME="_Toc305593278"></A><A NAME="_Toc305628750"></A><A NAME="_Toc312374073"></A><A NAME="_Toc472655055"></A><BR></P></DIV>
<A NAME="Heading474"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Stack and Stash<BR>as
templates<BR><A NAME="Index2652"></A><A NAME="Index2653"></A></H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The recurring <A NAME="Index2654"></A>
&#8220;ownership&#8221; problems with the <B>Stash</B> and <B>Stack</B>
container classes that have been revisited throughout this book come from the
fact that these containers haven&#8217;t been able to know exactly what types
they hold. The nearest they&#8217;ve come is the <B>Stack</B> &#8220;container
of <B>Object</B>&#8221; that was seen at the end of Chapter 15 in
<B>OStackTest.cpp</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">If the client programmer doesn&#8217;t
explicitly remove all the pointers to objects that are held in the container,
then the container should be able to correctly delete those pointers. That is to
say, the container &#8220;owns&#8221; any objects that haven&#8217;t been
removed, and is thus responsible for cleaning them up. The snag has been that
cleanup requires knowing the type of the object, and creating a generic
container class requires <I>not</I> knowing the type of the object. With
templates, however, we can write code that doesn&#8217;t know the type of the
object, and easily instantiate a new version of that container for every type
that we want to contain. The individual instantiated containers <I>do</I> know
the type of objects they hold and can thus call the correct destructor
(assuming, in the typical case where polymorphism is involved, that a virtual
destructor has been provided).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">For the <B>Stack</B> this turns out to be
quite simple since all of the member functions can be reasonably

⌨️ 快捷键说明

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