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

📄 chapter16.html

📁 《C++编程思想》中文版。。。。。。。。。。。。。
💻 HTML
📖 第 1 页 / 共 5 页
字号:
technique.</FONT><A NAME="_Toc312374067"></A><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><B>The Smalltalk solution</B>. Smalltalk
(and <A NAME="Index2600"></A>Java, following its example)
<A NAME="Index2601"></A>took a simple and straightforward approach: You want to
reuse code, so use inheritance.
<A NAME="Index2602"></A><A NAME="Index2603"></A><A NAME="Index2604"></A>To
implement this, each container class holds items of the generic base class
<B>Object</B> (similar to the example at the end of Chapter 15). But because the
library in Smalltalk is of such fundamental importance, you don&#8217;t ever
create a class from scratch. Instead, you must always inherit it from an
existing class. You find a class as close as possible to the one you want,
inherit from it, and make a few changes. Obviously, this is a benefit because it
minimizes your effort (and explains why you spend a lot of time learning the
class library before becoming an effective Smalltalk
programmer).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">But it also means that all classes in
Smalltalk end up being part of a single inheritance tree. You must inherit from
a branch of this tree when creating a new class. Most of the tree is already
there (it&#8217;s the Smalltalk class library), and at the root of the tree is a
class called <B>Object</B> &#8211; the same class that each Smalltalk container
holds.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This is a neat trick because it means
that every class in the Smalltalk (and
Java</FONT><A NAME="fnB59" HREF="#fn59">[59]</A><A NAME="Index2605"></A><FONT FACE="Georgia">)
class hierarchy is derived from <B>Object</B>, so every class can be held in
every container (including that container itself). This type of single-tree
hierarchy based on a fundamental generic type (often named <B>Object</B>, which
is also the case in Java) is referred to as an &#8220;object-based
hierarchy.&#8221; You may have heard this term and assumed it was some new
fundamental concept in OOP, like polymorphism. It simply refers to a class
hierarchy with <B>Object</B> (or some similar name) at its root and container
classes that hold <B>Object</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Because the Smalltalk class library had a
much longer history and experience behind it than did C++, and because the
original C++ compilers had <I>no</I> container class libraries, it seemed like a
good idea to duplicate the Smalltalk library in C++. This was done as an
experiment with an early C++
implementation</FONT><A NAME="fnB60" HREF="#fn60">[60]</A><A NAME="Index2606"></A><FONT FACE="Georgia">,
and because it represented a significant body of code, many people began using
it. In the process of trying to use the container classes, they discovered a
problem.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The problem was that in Smalltalk (and
most other OOP languages that I know of), all classes are automatically derived
from a single hierarchy, but this isn&#8217;t true in C++. You might have your
nice object-based hierarchy with its container classes, but then you might buy a
set of shape classes or aircraft classes from another vendor who didn&#8217;t
use that hierarchy. (For one thing, using that hierarchy imposes overhead, which
C programmers eschew.) How do you insert a separate class tree into the
container class in your object-based hierarchy? Here&#8217;s what the problem
looks like:</FONT><BR></P></DIV>
<DIV ALIGN="CENTER"><FONT FACE="Georgia"><IMG SRC="TIC2Vo21.gif"></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Because C++ supports multiple independent
hierarchies, Smalltalk&#8217;s object-based hierarchy does not work so
well.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The solution seemed obvious. If you can
have many inheritance hierarchies, then you should be able to inherit from more
than one class: <A NAME="Index2607"></A><A NAME="Index2608"></A>Multiple
inheritance will solve the problem. So you do the following (a similar example
was given at the end of Chapter 15):</FONT><BR></P></DIV>
<DIV ALIGN="CENTER"><FONT FACE="Georgia"><IMG SRC="TIC2Vo22.gif"></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Now <B>OShape</B> has
<B>Shape</B>&#8217;s characteristics and behaviors, but because it is also
derived from <B>Object</B> it can be placed in <B>Container</B>. The extra
inheritance into <B>OCircle</B>, <B>OSquare</B>, etc. is necessary so that those
classes can be upcast into <B>OShape </B>and thus retain the correct behavior.
You can see that things are rapidly getting messy.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Compiler vendors invented and included
their own object-based container-class hierarchies, most of which have since
been replaced by template versions. You can argue that multiple inheritance is
needed for solving general programming problems, but you&#8217;ll see in Volume
2 of this book that its complexity is best avoided except in special
cases.</FONT><A NAME="_Toc312374068"></A><A NAME="_Toc472655050"></A><BR></P></DIV>
<A NAME="Heading468"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
The template solution</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Although an object-based hierarchy with
multiple inheritance is conceptually straightforward, it turns out to be painful
to use. In his original
book</FONT><A NAME="fnB61" HREF="#fn61">[61]</A><A NAME="Index2609"></A><FONT FACE="Georgia">
Stroustrup demonstrated what he considered a preferable alternative to the
object-based hierarchy. Container classes were created as large preprocessor
macros<A NAME="Index2610"></A><A NAME="Index2611"></A> with arguments that could
be substituted with your desired type. When you wanted to create a container to
hold a particular type, you made a couple of macro calls.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Unfortunately, this approach was confused
by all the existing Smalltalk literature and programming experience, and it was
a bit unwieldy. Basically, nobody got it.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In the meantime, Stroustrup and the C++
team at Bell Labs had modified his original macro approach, simplifying it and
moving it from the domain of the preprocessor into the compiler. This new
code-substitution device is called a
<A NAME="Index2612"></A><A NAME="Index2613"></A><B>template</B></FONT><A NAME="fnB62" HREF="#fn62">[62]</A><FONT FACE="Georgia">,
and it represents a completely different way to reuse code. Instead of reusing
object code, as with inheritance and composition, a template reuses <I>source
code<A NAME="Index2614"></A></I>. The container no longer holds a generic base
class called <B>Object</B>, but instead it holds an unspecified parameter. When
you use a template, the parameter is substituted <I>by the compiler</I>, much
like the old macro approach, but cleaner and easier to use.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Now, instead of worrying about
inheritance or composition when you want to use a container class, you take the
template version of the container and stamp out a specific version for your
particular problem, like this:</FONT><BR></P></DIV>
<DIV ALIGN="CENTER"><FONT FACE="Georgia"><IMG SRC="TIC2Vo23.gif"></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The compiler does the work for you, and
you end up with exactly the container you need to do your job, rather than an
unwieldy inheritance hierarchy. In C++, the template implements the concept of a
<I>parameterized type</I>. Another benefit of the template approach is that the
novice programmer who may be unfamiliar or uncomfortable with inheritance can
still use canned container classes right away (as we&#8217;ve been doing with
<B>vector</B> throughout the
book).</FONT><A NAME="_Toc305593277"></A><A NAME="_Toc305628749"></A><A NAME="_Toc312374069"></A><A NAME="_Toc472655051"></A><BR></P></DIV>
<A NAME="Heading469"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Template syntax</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>template</B> keyword tells the
compiler that the class definition that follows will manipulate one or more
unspecified types. At the time the actual class code is generated from the
template, those types must be specified so that the compiler can substitute
them.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To demonstrate the syntax, here&#8217;s a
small example that produces a
<A NAME="Index2615"></A><A NAME="Index2616"></A>bounds-checked
array:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C16:Array.cpp</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&gt;
<font color=#0000ff>class</font> Array {
  <font color=#0000ff>enum</font> { size = 100 };
  T A[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> A[index];
  }
};

<font color=#0000ff>int</font> main() {
  Array&lt;<font color=#0000ff>int</font>&gt; ia;
  Array&lt;<font color=#0000ff>float</font>&gt; fa;
  <font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i &lt; 20; i++) {
    ia[i] = i * i;
    fa[i] = <font color=#0000ff>float</font>(i) * 1.414;
  }
  <font color=#0000ff>for</font>(<font color=#0000ff>int</font> j = 0; j &lt; 20; j++)
    cout &lt;&lt; j &lt;&lt; <font color=#004488>": "</font> &lt;&lt; ia[j]
         &lt;&lt; <font color=#004488>", "</font> &lt;&lt; fa[j] &lt;&lt; endl;
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can see that it looks like a normal
class except for the line</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>template</font>&lt;<font color=#0000ff>class</font> T&gt; </PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">which says that <B>T</B> is the
substitution parameter, and that it represents a type name. Also, you see
<B>T</B> used everywhere in the class where you would normally see the specific
type the container holds.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In <B>Array</B>, elements are inserted
<I>and</I> extracted with the same function: the overloaded <B>operator [ ]
<A NAME="Index2617"></A><A NAME="Index2618"></A><A NAME="Index2619"></A></B>. It
returns a reference, so it can be used on both sides of an equal sign (that is,
as both an <A NAME="Index2620"></A><I>lvalue</I> and an
<A NAME="Index2621"></A><I>rvalue</I>). Notice that if the index is out of
bounds, the <A NAME="Index2622"></A><B>require(&#160;)</B> function is used to
print a message. Since <B>operator[] </B>is an <B>inline</B>,<B> </B>you could
use this approach to guarantee that no array-bounds violations occur, then
remove the <B>require(&#160;)</B> for the shipping code.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In <B>main(&#160;)</B>, you can see how
easy it is to create <B>Array</B>s that hold different types of objects. When
you say</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE>Array&lt;<font color=#0000ff>int</font>&gt; ia;
Array&lt;<font color=#0000ff>float</font>&gt; fa;</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">the compiler expands the <B>Array</B>
template (this is called
<I>instantiation<A NAME="Index2623"></A><A NAME="Index2624"></A></I>) twice, to
create two new <I>generated
classes<A NAME="Index2625"></A><A NAME="Index2626"></A></I>, which you can think
of as <B>Array_int</B> and <B>Array_float</B>. (Different compilers may decorate
the names in different ways.) These are classes just like the ones you would
have produced if you had performed the substitution by hand, except that the
compiler creates them for you as you define the objects <B>ia</B> and <B>fa</B>.
Also note that duplicate class
definitions<A NAME="Index2627"></A><A NAME="Index2628"></A><A NAME="Index2629"></A>
are either avoided by the compiler or merged by the
linker.</FONT><A NAME="_Toc312374070"></A><A NAME="_Toc472655052"></A><BR></P></DIV>
<A NAME="Heading470"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Non-inline function definitions</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Of course, there are times when
you&#8217;ll want to have
<A NAME="Index2630"></A><A NAME="Index2631"></A><A NAME="Index2632"></A><A NAME="Index2633"></A>non-inline
member function definitions. In this case, the compiler needs to see the
<B>template</B> declaration before the member function definition. Here&#8217;s
the example above, modified to show the non-inline member
definition:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C16:Array2.cpp</font>
<font color=#009900>// Non-inline template definition</font>
#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> Array {
  <font color=#0000ff>enum</font> { size = 100 };
  T A[size];
<font color=#0000ff>public</font>:
  T&amp; <font color=#0000ff>operator</font>[](<font color=#0000ff>int</font> index);
};

<font color=#0000ff>template</font>&lt;<font color=#0000ff>class</font> T&gt;
T&amp; Array&lt;T&gt;::<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> A[index];
}

<font color=#0000ff>int</font> main() {
  Array&lt;<font color=#0000ff>float</font>&gt; fa;
  fa[0] = 1.414;
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Any reference to a template&#8217;s class

⌨️ 快捷键说明

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