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

📄 ei43.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
📖 第 1 页 / 共 4 页
字号:
<P><A NAME="dingp33"></A>
You come to discover that <CODE>PersonInfo</CODE>, however, was designed to facilitate the process of printing database fields in various formats, with the beginning and end of each field value delimited by special strings. By default, the opening and closing delimiters for field values are <A NAME="p203"></A>braces, so the field value "Ring-tailed Lemur" would be formatted this <NOBR>way:<SCRIPT>create_link(33);</SCRIPT>
</NOBR></P></P>
<A NAME="22766"></A>
<UL><PRE>[Ring-tailed Lemur]
</PRE>
</UL><A NAME="223048"></A>
<P><A NAME="dingp34"></A>
In recognition of the fact that braces are not universally desired by clients of <CODE>PersonInfo</CODE>, the virtual functions <CODE>valueDelimOpen</CODE> and <CODE>valueDelimClose</CODE> allow derived classes to specify their own opening and closing delimiter strings. The implementations of <CODE>PersonInfo</CODE>'s <CODE>theName</CODE>, <CODE>theBirthDate</CODE>, <CODE>theAddress</CODE>, and <CODE>theNationality</CODE> call these virtual functions to add the appropriate delimiters to the values they return. Using <CODE>PersonInfo::name</CODE> as an example, the code looks like <NOBR>this:<SCRIPT>create_link(34);</SCRIPT>
</NOBR></P>
<A NAME="22686"></A>
<UL><PRE>const char * PersonInfo::valueDelimOpen() const
{
  return "[";                   // default opening delimiter
}
</PRE>
</UL><A NAME="22726"></A>
<UL><PRE>const char * PersonInfo::valueDelimClose() const
{
  return "]";                   // default closing delimiter
}
</PRE>
</UL><A NAME="22654"></A>
<UL><PRE>const char * PersonInfo::theName() const
{
  // reserve buffer for return value. Because this is
  // static, it's automatically initialized to all zeros
  static char value[MAX_FORMATTED_FIELD_VALUE_LENGTH];
</PRE>
</UL><A NAME="22730"></A>
<UL><PRE>  // write opening delimiter
  strcpy(value, valueDelimOpen());
</PRE>
</UL><A NAME="22731"></A>
<UL><PRE>  <I>append to the string in value this object's name field</I>
</PRE>
</UL><A NAME="22735"></A>
<UL><PRE>  // write closing delimiter
  strcat(value, valueDelimClose());
</PRE>
</UL><A NAME="22736"></A>
<UL><PRE>  return value;
}
</PRE>
</UL><A NAME="22655"></A>
<P><A NAME="dingp35"></A>
One might quibble with the design of <CODE>PersonInfo::theName</CODE> (especially the use of a fixed-size static buffer &#151; see <A HREF="./EI23_FR.HTM#6210" TARGET="_top">Item 23</A>), but set your quibbles aside and focus instead on this: <CODE>theName</CODE> calls <CODE>valueDelimOpen</CODE> to generate the opening delimiter of the string it will return, then it generates the name value itself, then it calls <CODE>valueDelimClose</CODE>. Because <CODE>valueDelimOpen</CODE> and <CODE>valueDelimClose</CODE> are virtual functions, the result returned by <CODE>theName</CODE> is dependent not only on <CODE>PersonInfo</CODE>, but also on the classes derived from <CODE>PersonInfo</CODE>.<SCRIPT>create_link(35);</SCRIPT>
</P>
<A NAME="22780"></A>
<P><A NAME="dingp36"></A>
As the implementer of <CODE>MyPerson</CODE>, that's good news, because while perusing the fine print in the <CODE>Person</CODE> documentation, you discover that <CODE>name</CODE> and its sister member functions are required to return un<A NAME="p204"></A>adorned values, i.e., no delimiters are allowed. That is, if a person is from Madagascar, a call to that person's <CODE>nationality</CODE> function should return "Madagascar", not <NOBR>"[Madagascar]".<SCRIPT>create_link(36);</SCRIPT>
</NOBR></P>
<A NAME="22788"></A>
<P><A NAME="dingp37"></A>
The relationship between <CODE>MyPerson</CODE> and <CODE>PersonInfo</CODE> is that <CODE>PersonInfo</CODE> happens to have some functions that make <CODE>MyPerson</CODE> easier to implement. That's all. There's no isa or has-a relationship anywhere in sight. Their relationship is thus is-implemented-in-terms-of, and we know that can be represented in two ways: via layering (see <A HREF="./EI40_FR.HTM#7424" TARGET="_top">Item 40</A>) and via private inheritance (see <A HREF="./EI42_FR.HTM#21052" TARGET="_top">Item 42</A>). <A HREF="./EI42_FR.HTM#21052" TARGET="_top">Item 42</A> points out that layering is the generally preferred approach, but private inheritance is necessary if virtual functions are to be redefined. In this case, <CODE>MyPerson</CODE> needs to redefine <CODE>valueDelimOpen</CODE> and <CODE>valueDelimClose</CODE>, so layering won't do and private inheritance it must be: <CODE>MyPerson</CODE> must privately inherit from <CODE>PersonInfo</CODE>.<SCRIPT>create_link(37);</SCRIPT>
</P>
<A NAME="22834"></A>
<P><A NAME="dingp38"></A>
But <CODE>MyPerson</CODE> must also implement the <CODE>Person</CODE> interface, and that calls for public inheritance. This leads to one reasonable application of multiple inheritance: combine public inheritance of an interface with private inheritance of an <NOBR>implementation:<SCRIPT>create_link(38);</SCRIPT>
</NOBR></P>
<A NAME="22846"></A>
<UL><PRE>
class Person {                        // this class specifies
public:                               // the interface to be
  virtual ~Person();                  // implemented
</PRE>
</UL><A NAME="22847"></A>
<UL><PRE>  virtual string name() const = 0;
  virtual string birthDate() const = 0;
  virtual string address() const = 0;
  virtual string nationality() const = 0;
};
</PRE>
</UL><A NAME="22864"></A>
<UL><PRE>
class DatabaseID { ... };             // used below; details
                                      // are unimportant
</PRE>
</UL><A NAME="22851"></A>
<UL><PRE>
class PersonInfo {                    // this class has functions
public:                               // useful in implementing
  PersonInfo(DatabaseID pid);         // the Person interface
  virtual ~PersonInfo();
</PRE>
</UL><A NAME="22852"></A>
<UL><PRE>  virtual const char * theName() const;
  virtual const char * theBirthDate() const;
  virtual const char * theAddress() const;
  virtual const char * theNationality() const;
</PRE>
</UL><A NAME="22853"></A>
<UL><PRE>  virtual const char * valueDelimOpen() const;
  virtual const char * valueDelimClose() const;
</PRE>
</UL><A NAME="22854"></A>
<UL><PRE>  ...
</PRE>
</UL><A NAME="22855"></A>
<UL><PRE>};
</PRE>
</UL><A NAME="22874"></A>
<UL><PRE><A NAME="p205"></A>
class MyPerson: public Person,        // note use of
                private PersonInfo {  // multiple inheritance
public:
  MyPerson(DatabaseID pid): PersonInfo(pid) {}
</PRE>
</UL><A NAME="22896"></A>
<UL><PRE>  // redefinitions of inherited virtual delimiter functions
  const char * valueDelimOpen() const { return ""; }
  const char * valueDelimClose() const { return ""; }
</PRE>
</UL><A NAME="22878"></A>
<UL><PRE>  // implementations of the required Person member functions
  string name() const
  { return PersonInfo::theName(); }
</PRE>
</UL><A NAME="31842"></A>
<UL><PRE>  string birthDate() const
  { return PersonInfo::theBirthDate(); }
</PRE>
</UL><A NAME="31843"></A>
<UL><PRE>  string address() const
  { return PersonInfo::theAddress(); }
</PRE>
</UL><A NAME="31844"></A>
<UL><PRE>  string nationality() const
  { return PersonInfo::theNationality(); }
};
</PRE>
</UL><A NAME="8021"></A>
<P><A NAME="dingp39"></A>Graphically, it looks like <NOBR>this:<SCRIPT>create_link(39);</SCRIPT>
</NOBR></P>

<SPAN ID="Image5of1" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_205A1.GIF" BORDER=0></SPAN>
<SPAN ID="Image5of2" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_205A2.GIF" BORDER=0></SPAN>
<SPAN ID="Image5of3" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_205A3.GIF" BORDER=0></SPAN>
<SPAN ID="Image5of4" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_205A4.GIF" BORDER=0></SPAN>
<SPAN ID="Image5of5" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_205A5.GIF" BORDER=0></SPAN>

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

<P><A NAME="dingp40"></A><A NAME="8032"></A>This kind of example demonstrates that MI can be both useful and comprehensible, although it's no accident that the dreaded diamond-shaped inheritance graph is conspicuously <NOBR>absent.<SCRIPT>create_link(40);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp41"></A><A NAME="23125"></A>
Still, you must guard against temptation. Sometimes you can fall into the trap of using MI to make a quick fix to an inheritance hierarchy that would be better served by a more fundamental redesign. For example, suppose you're working with a hierarchy for animated cartoon characters. At least conceptually, it makes sense for any kind of character to dance and sing, but the way in which each type of character performs these activities differs. Furthermore, the default behavior for singing and dancing is to do <NOBR>nothing.<SCRIPT>create_link(41);</SCRIPT>
</NOBR></P>
<A NAME="8034"></A>
<P><A NAME="dingp42"></A>
The way to say all that in C++ is like <NOBR>this:<SCRIPT>create_link(42);</SCRIPT>
</NOBR></P>
<A NAME="8036"></A>
<UL><PRE>class CartoonCharacter {
public:
  virtual void dance() {}
  virtual void sing() {}
};
</PRE>
</UL><A NAME="8037"></A>
<A NAME="p206"></A>Virtual functions naturally model the constraint that dancing and singing make sense for all <CODE>CartoonCharacter</CODE> objects. Do-nothing default behavior is expressed by the empty definitions of those functions in the class (see <A HREF="./EI36_FR.HTM#7007" TARGET="_top">Item 36</A>).
<A NAME="8041"></A>
<A NAME="dingp43"></A>Suppose a particular type of cartoon character is a grasshopper, which dances and sings in its own particular way:<SCRIPT>create_link(43);</SCRIPT>

<A NAME="8043"></A>
<UL><PRE>class Grasshopper: public CartoonCharacter {
public:
  virtual void dance();    // definition is elsewhere
  virtual void sing();     // definition is elsewhere
};
</PRE>
</UL><A NAME="8044"></A>
<P><A NAME="dingp44"></A>
Now suppose that after implementing the <CODE>Grasshopper</CODE> class, you decide you also need a class for <NOBR>crickets:<SCRIPT>create_link(44);</SCRIPT>
</NOBR></P>

⌨️ 快捷键说明

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