📄 chapter16.html
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<!--
This document was converted from RTF source:
By rtftohtml 4.19
See http://www.sunpack.com/RTF
Filename:TIC2Vone.rtf
Application Directory:C:\TOOLS\RTF2HTML\
Subject:
Author:Bruce Eckel
Operator:Bruce Eckel
Document Comments:
Version Comments:
Comments:
Keywords:
Translation Date:09/27/2001
Translation Time:05:26:00
Translation Platform:Win32
Number of Output files:22
This File:Chapter16.html
SplitDepth=1
SkipNavPanel=1
SkipLeadingToc=1
SkipTrailingToc=1
GenContents=1
GenFrames=1
GenIndex=1
-->
<HEAD lang="en"><META http-equiv="Content-Type" content="text/html">
<TITLE>16: Introduction to Templates</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF"><DIV ALIGN="CENTER">
<a href="http://www.MindView.net">
<img src="mindview-head.gif" alt="MindView Inc." BORDER = "0"></a>
<CENTER>
<FONT FACE="Verdana" size = "-1">
[ <a href="README-HTML.txt">Viewing Hints</a> ]
[ <a href="http://www.mindview.net/CPPServices/SolutionGuide.html">Exercise Solutions</a> ]
[ <a href="http://www.mindview.net/ThinkingInCPP2e.html">Volume 2</a> ]
[ <a href="http://www.mindview.net/MailingList.html">Free Newsletter</a> ] <br>
[ <a href="http://www.mindview.net/CPPServices/#PublicSeminars">Seminars</a> ]
[ <a href="http://www.mindview.net/CPPServices/#SeminarsOnCD">Seminars on CD ROM</a> ]
[ <a href="http://www.mindview.net/CPPServices/#ConsultingServices">Consulting</a> ]
</FONT>
<H2><FONT FACE="Verdana">
Thinking in C++, 2nd ed. Volume 1</FONT></H2></FONT>
<H3><FONT FACE="Verdana">©2000 by Bruce Eckel</FONT></H3></FONT>
<FONT FACE="Verdana" size = "-1">
[ <a href="Chapter15.html">Previous Chapter</a> ]
[ <a href="Contents.html">Table of Contents</a> ]
[ <a href="DocIndex.html">Index</a> ]
[ <a href="AppendixA.html">Next Chapter</a> ]
</FONT>
</CENTER>
</P></DIV><A NAME="_Toc472655046"></A><FONT FACE = "Verdana"><H1 ALIGN="LEFT">
16: Introduction to Templates</H1></FONT>
<A NAME="Heading464"></A><FONT FACE = "Verdana"><H1 ALIGN="LEFT">
</H1></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Verdana" SIZE=4>Inheritance and composition
provide a way to <A NAME="Index2588"></A>reuse object code. The
<A NAME="Index2589"></A><A NAME="Index2590"></A><I>template</I> feature in C++
provides </FONT><BR><FONT FACE="Verdana" SIZE=4>a way to reuse <I>source</I>
code.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Although C++ templates are a
general-purpose programming tool, when they were introduced in the language,
they seemed to discourage the use of object-based container-class hierarchies
(demonstrated at the end of Chapter 15). For example, the Standard C++
containers and algorithms (explained in two chapters of Volume 2 of this book,
downloadable from <I>www.BruceEckel.com</I>) are built exclusively with
templates and are relatively easy for the programmer to use.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This chapter not only demonstrates the
basics of templates, it is also an introduction to containers, which are
fundamental components of object-oriented programming and are almost completely
realized through the containers in the Standard C++ Library. You’ll see
that this book has been using container examples – the <B>Stash </B>and
<B>Stack </B>– throughout, precisely to get you comfortable with
containers; in this chapter the concept of the <I>iterator</I> will also be
added. Although containers are ideal examples for use with templates, in Volume
2 (which has an advanced templates chapter) you’ll learn that there are
many other uses for templates as
well.</FONT><A NAME="_Toc305593275"></A><A NAME="_Toc305628747"></A><A NAME="_Toc312374063"></A><A NAME="_Toc472655047"></A><BR></P></DIV>
<A NAME="Heading465"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Containers<BR><A NAME="Index2591"></A><A NAME="Index2592"></A></H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Suppose you want to create a
<A NAME="Index2593"></A>stack, as we have been doing throughout the book. This
stack class will hold <B>int</B>s, to keep it simple:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C16:IntStack.cpp</font>
<font color=#009900>// Simple integer stack</font>
<font color=#009900>//{L} fibonacci</font>
#include <font color=#004488>"fibonacci.h"</font>
#include <font color=#004488>"../require.h"</font>
#include <iostream>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>class</font> IntStack {
<font color=#0000ff>enum</font> { ssize = 100 };
<font color=#0000ff>int</font> stack[ssize];
<font color=#0000ff>int</font> top;
<font color=#0000ff>public</font>:
IntStack() : top(0) {}
<font color=#0000ff>void</font> push(<font color=#0000ff>int</font> i) {
require(top < ssize, <font color=#004488>"Too many push()es"</font>);
stack[top++] = i;
}
<font color=#0000ff>int</font> pop() {
require(top > 0, <font color=#004488>"Too many pop()s"</font>);
<font color=#0000ff>return</font> stack[--top];
}
};
<font color=#0000ff>int</font> main() {
IntStack is;
<font color=#009900>// Add some Fibonacci numbers, for interest:</font>
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < 20; i++)
is.push(fibonacci(i));
<font color=#009900>// Pop & print them:</font>
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> k = 0; k < 20; k++)
cout << is.pop() << endl;
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The class <B>IntStack</B> is a trivial
example of a push-down stack. For simplicity it has been created here with a
fixed size, but you can also modify it to automatically expand by allocating
memory off the heap, as in the <B>Stack</B> class that has been examined
throughout the book.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><B>main( )</B> adds some integers to
the stack, and pops them off again. To make the example more interesting, the
integers are created with the <B>fibonacci( )<A NAME="Index2594"></A></B>
function, which generates the traditional rabbit-reproduction numbers. Here is
the header file that declares the function:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C16:fibonacci.h</font>
<font color=#009900>// Fibonacci number generator</font>
<font color=#0000ff>int</font> fibonacci(<font color=#0000ff>int</font> n); <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here’s the
implementation:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C16:fibonacci.cpp {O}</font>
#include <font color=#004488>"../require.h"</font>
<font color=#0000ff>int</font> fibonacci(<font color=#0000ff>int</font> n) {
<font color=#0000ff>const</font> <font color=#0000ff>int</font> sz = 100;
require(n < sz);
<font color=#0000ff>static</font> <font color=#0000ff>int</font> f[sz]; <font color=#009900>// Initialized to zero</font>
f[0] = f[1] = 1;
<font color=#009900>// Scan for unfilled array elements:</font>
<font color=#0000ff>int</font> i;
<font color=#0000ff>for</font>(i = 0; i < sz; i++)
<font color=#0000ff>if</font>(f[i] == 0) <font color=#0000ff>break</font>;
<font color=#0000ff>while</font>(i <= n) {
f[i] = f[i-1] + f[i-2];
i++;
}
<font color=#0000ff>return</font> f[n];
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This is a fairly efficient
implementation, because it never generates the numbers more than once. It uses a
<A NAME="Index2595"></A><A NAME="Index2596"></A><B>static</B> array of
<B>int</B>, and relies on the fact that the compiler will initialize a
<B>static</B> array to zero. The first <B>for</B> loop moves the index <B>i</B>
to where the first array element is zero, then a <B>while </B>loop adds
Fibonacci numbers to the array until the desired element is reached. But notice
that if the Fibonacci numbers through element <B>n</B> are already initialized,
it skips the <B>while</B> loop
altogether.</FONT><A NAME="_Toc312374064"></A><A NAME="_Toc472655048"></A><BR></P></DIV>
<A NAME="Heading466"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
The need for containers</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Obviously, an integer stack isn’t a
crucial tool. The real need for containers comes when you start making objects
on the heap using <B>new</B> and destroying them with <B>delete</B>. In the
general programming problem, you don’t know how many objects you’re
going to need while you’re writing the program. For example, in an
air-traffic control system you don’t want to limit the number of planes
your system can handle. You don’t want the program to abort just because
you exceed some number. In a computer-aided design system, you’re dealing
with lots of shapes, but only the user determines (at runtime) exactly how many
shapes you’re going to need. Once you notice this tendency, you’ll
discover lots of examples in your own programming situations.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">C programmers who rely on virtual memory
to handle their “memory management” often find the idea of
<A NAME="Index2597"></A><A NAME="Index2598"></A><A NAME="Index2599"></A><B>new</B>,
<B>delete,</B> and container classes disturbing. Apparently, one practice in C
is to create a huge global array, larger than anything the program would appear
to need. This may not require much thought (or awareness of
<B>malloc( )</B> and <B>free( )</B>), but it produces programs that
don’t port well and that hide subtle bugs.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In addition, if you create a huge global
array of objects in C++, the constructor and destructor overhead can slow things
down significantly. The C++ approach works much better: When you need an object,
create it with <B>new,</B> and put its pointer in a container. Later on, fish it
out and do something to it. This way, you create only the objects you absolutely
need. And usually you don’t have all the initialization conditions
available at the start-up of the program. <B>new</B> allows you to wait until
something happens in the environment before you can actually create the
object.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">So in the most common situation,
you’ll make a container that holds pointers to some objects of interest.
You will create those objects using <B>new</B> and put the resulting pointer in
the container (potentially upcasting it in the process), pulling it out later
when you want to do something with the object. This technique produces the most
flexible, general sort of
program.</FONT><A NAME="_Toc305593276"></A><A NAME="_Toc305628748"></A><A NAME="_Toc312374065"></A><A NAME="_Toc472655049"></A><BR></P></DIV>
<A NAME="Heading467"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Overview of templates</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Now a problem arises. You have an
<B>IntStack</B>, which holds integers. But you want a stack that holds shapes or
aircraft or plants or something else. Reinventing your source code every time
doesn’t seem like a very intelligent approach with a language that touts
reusability. There must be a better way.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">There are three techniques for source
code reuse in this situation: the C way, presented here for contrast; the
Smalltalk approach, which significantly affected C++; and the C++ approach:
templates.</FONT><A NAME="_Toc312374066"></A><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><B>The C solution</B>. Of course
you’re trying to get away from the C approach because it’s messy and
error prone and completely inelegant. In this approach, you copy the source code
for a <B>Stack</B> and make modifications by hand, introducing new errors in the
process. This is certainly not a very productive
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -