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

📄 mi31.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
📖 第 1 页 / 共 5 页
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/frameset.dtd">
<HTML LANG="EN">
<HEAD>
<TITLE>More Effective C++ | Item 31: Making functions virtual with respect to more than one object</TITLE>
<LINK REL=STYLESHEET HREF=../INTRO/ECMEC.CSS>

<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/COOKIE.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">var imagemax = 4; setCurrentMax(4);</SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/IMGDOC.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/NSIMGDOC.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/DINGBATS.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">
var dingbase = "MI31_DIR.HTM";
var dingtext = "Item M31, P";
if (self == top) {
 top.location.replace(dingbase + this.location.hash);
}
</SCRIPT>

</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" ONLOAD="setResize()">
<!-- SectionName="M31: Implementing multiple dispatch" -->
<A NAME="34883"></A>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./MI30_FR.HTM" TARGET="_top">Item 30: Proxy classes</A> &nbsp;&nbsp;<BR>&nbsp;&nbsp;Continue to <A HREF="./MMISC_FR.HTM" TARGET="_top">Miscellany</A></FONT></DIV>


<P><A NAME="dingp1"></A><font ID="mititle">Item 31: &nbsp;Making functions virtual with respect to more than one object.</font><SCRIPT>create_link(1);</SCRIPT>
</P>

<A NAME="72185"></A>

<P><A NAME="dingp2"></A><A NAME="34884"></A>
Sometimes, to borrow a phrase from Jacqueline Susann, once is not enough. Suppose, for example, you're bucking for one of those high-profile, high-prestige, high-paying programming jobs at that famous software company in Redmond, Washington &#151; by which of course I mean Nintendo. To bring yourself to the attention of Nintendo's management, you might decide to write a video game. Such a game might take place in outer space and involve space ships, space stations, and <NOBR>asteroids.<SCRIPT>create_link(2);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp3"></A><A NAME="34885"></A>
As the ships, stations, and asteroids whiz around in your artificial world, they naturally run the risk of colliding with one another. Let's assume the rules for such collisions are as <NOBR>follows:<SCRIPT>create_link(3);</SCRIPT>
</NOBR></P>

<UL><A NAME="34886"></A>
<A NAME="dingp4"></A><LI>If a ship and a station collide at low velocity, the ship docks at the station. Otherwise the ship and the station sustain damage that's proportional to the speed at which they collide.<SCRIPT>create_link(4);</SCRIPT>

<A NAME="34887"></A>
<A NAME="dingp5"></A><LI>If a ship and a ship or a station and a station collide, both participants in the collision sustain damage that's proportional to the speed at which they hit.<SCRIPT>create_link(5);</SCRIPT>

<A NAME="34888"></A>
<A NAME="dingp6"></A><LI>If a small asteroid collides with a ship or a station, the asteroid is destroyed. If it's a big asteroid, the ship or the station is destroyed.<SCRIPT>create_link(6);</SCRIPT>

<A NAME="34889"></A>
<A NAME="dingp7"></A><LI>If an asteroid collides with another asteroid, both break into pieces and scatter little baby asteroids in all directions.<SCRIPT>create_link(7);</SCRIPT>

</UL>

<P><A NAME="dingp8"></A><A NAME="34890"></A>
This may sound like a dull game, but it suffices for our purpose here, which is to consider how to structure the C++ code that handles collisions between <NOBR>objects.<SCRIPT>create_link(8);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp9"></A><A NAME="34891"></A>
We begin by noting that ships, stations, and asteroids share some common features. If nothing else, they're all in motion, so they all have a velocity that describes that motion. Given this commonality, it is natural to define a base class from which they all inherit. In practice, such a class is almost invariably an abstract base class, and, if you heed the <A NAME="p229"></A>warning I give in <a href="./MI33_FR.HTM#10947" TARGET="_top">Item 33</A>, base classes are always abstract. The hierarchy might therefore look like <NOBR>this:<SCRIPT>create_link(9);</SCRIPT>
</NOBR></P>

<SPAN ID="Image1of1" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_229A1.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of2" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_229A2.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of3" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_229A3.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of4" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_229A4.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of5" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_229A5.GIF" BORDER=0></SPAN>

<SPAN ID="Image1of6" STYLE="position: relative; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_229A5.GIF" BORDER=0></SPAN>

<A NAME="34913"></A>
<UL><PRE>class GameObject { ... };
</PRE>
</UL><A NAME="34914"></A>
<UL><PRE>class SpaceShip: public GameObject { ... };
</PRE>
</UL><A NAME="34915"></A>
<UL><PRE>class SpaceStation: public GameObject { ... };
</PRE>
</UL><A NAME="34916"></A>
<UL><PRE>class Asteroid: public GameObject { ... };
</PRE>
</UL><A NAME="34917"></A>

<P><A NAME="dingp10"></A>Now, suppose you're deep in the bowels of your program, writing the code to check for and handle object collisions. You might come up with a function that looks something like <NOBR>this:<SCRIPT>create_link(10);</SCRIPT>
</NOBR></P><A NAME="34918"></A>
<UL><PRE>void checkForCollision(GameObject&amp; object1,
                       GameObject&amp; object2)
{
  if (theyJustCollided(object1, object2)) {
    processCollision(object1, object2);
  }
  else {
    ...
  }
}
</PRE>
</UL>

<P><A NAME="dingp11"></A><A NAME="34919"></A>
This is where the programming challenge becomes apparent. When you call <CODE>processCollision</CODE>, you know that <CODE>object1</CODE> and <CODE>object2</CODE> just collided, and you know that what happens in that collision depends on what <CODE>object1</CODE> really is and what <CODE>object2</CODE> really is, but you don't know what kinds of objects they really are; all you know is that they're both <CODE>GameObject</CODE>s. If the collision processing depended only on the <A HREF="./MIINTRFR.HTM#72671" TARGET="_top">dynamic type</a> of <CODE>object1</CODE>, you could make <CODE>processCollision</CODE> virtual in <CODE>GameObject</CODE> and call <CODE>object1.processCollision(object2)</CODE>. You could do the same thing with <CODE>object2</CODE> if the details of the collision depended only on its dynamic type. What happens in the collision, however, depends on <I>both</I> their dynamic types. A function call that's virtual on only one object, you see, is not <NOBR>enough.<SCRIPT>create_link(11);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp12"></A><A NAME="34920"></A>
What you need is a kind of function whose behavior is somehow virtual on the types of more than one object. C++ offers no such function. Nev<A NAME="p230"></A>ertheless, you still have to implement the behavior required above. The question, then, is how you are going to do <NOBR>it.<SCRIPT>create_link(12);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp13"></A><A NAME="34921"></A>
One possibility is to scrap the use of C++ and choose another programming language. You could turn to CLOS, for example, the Common Lisp Object System. CLOS supports what is possibly the most general object-oriented function-invocation mechanism one can imagine: <I>multi-methods</I>. A multi-method is a function that's virtual on as many parameters as you'd like, and CLOS goes even further by giving you substantial control over how calls to overloaded multi-methods are <NOBR>resolved.<SCRIPT>create_link(13);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp14"></A><A NAME="34923"></A>
Let us assume, however, that you must implement your game in C++ &#151; that you must come up with your own way of implementing what is commonly referred to as <I>double-dispatching</I>. (The name comes from the object-oriented programming community, where what C++ programmers know as a virtual function call is termed a "message dispatch." A call that's virtual on two parameters is implemented through a "double dispatch." The generalization of this &#151; a function acting virtual on several parameters &#151; is called <I>multiple dispatch</I>.) There are several approaches you might consider. None is without its disadvantages, but that shouldn't surprise you. C++ offers no direct support for double-dispatching, so you must yourself do the work compilers do when they implement virtual functions (see <a href="./MI24_FR.HTM#41284" TARGET="_top">Item 24</A>). If that were easy to do, we'd probably all be doing it ourselves and simply programming in C. We aren't and we don't, so fasten your seat belts, it's going to be a bumpy <NOBR>ride.<SCRIPT>create_link(14);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp15"></A><font ID="mhtitle">Using Virtual Functions and RTTI</font><SCRIPT>create_link(15);</SCRIPT>
</P>

<P><A NAME="dingp16"></A><A NAME="34927"></A>
Virtual functions implement a single dispatch; that's half of what we need; and compilers do virtual functions for us, so we begin by declaring a virtual function <CODE>collide</CODE> in <CODE>GameObject</CODE>. This function is overridden in the derived classes in the usual <NOBR>manner:<SCRIPT>create_link(16);</SCRIPT>
</NOBR></P><A NAME="34928"></A>

<UL><PRE>class GameObject {
public:
  virtual void collide(GameObject&amp; otherObject) = 0;
  ...
};
<A NAME="34929"></A>
class SpaceShip: public GameObject {
public:
  virtual void collide(GameObject&amp; otherObject);
  ...
};
</PRE>
</UL>

<P><A NAME="dingp17"></A><A NAME="34930"></A>
Here I'm showing only the derived class <CODE>SpaceShip</CODE>, but <CODE>SpaceStation</CODE> and <CODE>Asteroid</CODE> are handled in exactly the same <NOBR>manner.<SCRIPT>create_link(17);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp18"></A><A NAME="34931"></A>
<A NAME="p231"></A>The most common approach to double-dispatching returns us to the unforgiving world of virtual function emulation via chains of <CODE>if</CODE>-<CODE>then</CODE>-<CODE>else</CODE>s. In this harsh world, we first discover the real type of <CODE>otherObject</CODE>, then we test it against all the <NOBR>possibilities:<SCRIPT>create_link(18);</SCRIPT>
</NOBR></P><A NAME="34932"></A>
<UL><PRE>// if we collide with an object of unknown type, we
// throw an exception of this type:
class CollisionWithUnknownObject {
public:
  CollisionWithUnknownObject(GameObject&amp; whatWeHit);
  ...
};
<A NAME="34933"></A>
void SpaceShip::collide(GameObject&amp; otherObject)
{
  const type_info&amp; objectType = typeid(otherObject);
<A NAME="34934"></A>
  if (objectType == typeid(SpaceShip)) {
    SpaceShip&amp; ss = static_cast&lt;SpaceShip&amp;&gt;(otherObject);
<A NAME="34935"></A>
  <I>  process a SpaceShip-SpaceShip collision;</I>
<A NAME="34936"></A>
  }
<A NAME="34937"></A>
  else if (objectType == typeid(SpaceStation)) {
    SpaceStation&amp; ss =
      static_cast&lt;SpaceStation&amp;&gt;(otherObject);
<A NAME="34938"></A>
  <I>  process a SpaceShip-SpaceStation collision;</I>
<A NAME="34939"></A>
  }
<A NAME="34940"></A>
  else if (objectType == typeid(Asteroid)) {
    Asteroid&amp; a = static_cast&lt;Asteroid&amp;&gt;(otherObject);
<A NAME="34941"></A>
  <I>  process a SpaceShip-Asteroid collision;</I>
<A NAME="34942"></A>
  }
<A NAME="34943"></A>
  else {

⌨️ 快捷键说明

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