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

📄 chap10.htm

📁 This is the second part of that lab manual to teach you how to make real-time programme and how to d
💻 HTM
📖 第 1 页 / 共 5 页
字号:
    C.write(data);
  } <font color=#009900>// Closes file</font>
  ifstream data(<font color=#004488>"data.dat"</font>);
  assure(data, <font color=#004488>"data.dat"</font>);
  Conglomerate C;
  C.read(data);
  cout &lt;&lt; <font color=#004488>"after storage: "</font> &lt;&lt; endl;
  C.print();
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The pure virtual functions in
<B>Persistent</B> must be redefined in the derived classes to perform the proper
reading and writing. If you already knew that <B>Data</B> would be persistent,
you could inherit directly from <B>Persistent</B> and redefine the functions
there, thus eliminating the need for multiple inheritance. This example is based
on the idea that you don&#8217;t own the code for <B>Data</B>, that it was
created elsewhere and may be part of another class hierarchy so you don&#8217;t
have control over its inheritance. However, for this scheme to work correctly
you must have access to the underlying implementation so it can be stored; thus
the use of <B>protected</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The classes <B>WData1</B> and
<B>WData2</B> use familiar iostream inserters and extractors to store and
retrieve the <B>protected</B> data in <B>Data</B> to and from the iostream
object. In <B>write(&#160;)</B>, you can see that spaces are added after each
floating point number is written; these are necessary to allow parsing of the
data on input.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The class <B>Conglomerate</B> not only
inherits from <B>Data</B>, it also has member objects of type <B>WData1</B> and
<B>WData2</B>, as well as a pointer to a character string. In addition, all the
classes that inherit from <B>Persistent</B> also contain a VPTR, so this example
shows the kind of problem you&#8217;ll actually encounter when using
persistence.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When you create <B>write(&#160;)</B> and
<B>read(&#160;)</B> function pairs, the <B>read(&#160;)</B> must exactly mirror
what happens during the <B>write(&#160;)</B>, so <B>read(&#160;)</B> pulls the
bits off the disk the same way they were placed there by <B>write(&#160;)</B>.
Here, the first problem that&#8217;s tackled is the <B>char*</B>, which points
to a string of any length. The size of the string is calculated and stored on
disk as an <B>int</B> (followed by a space to enable parsing) to allow the
<B>read(&#160;)</B> function to allocate the correct amount of
storage.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When you have subobjects that have
<B>read(&#160;)</B> and <B>write(&#160;)</B> member functions, all you need to
do is call those functions in the new <B>read(&#160;)</B> and
<B>write(&#160;)</B> functions. This is followed by direct storage of the
members in the base class.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">People have gone to great lengths to
automate persistence, for example, by creating modified preprocessors to support
a &#8220;persistent&#8221; keyword to be applied when defining a class. One can
imagine a more elegant approach than the one shown here for implementing
persistence, but it has the advantage that it works under all implementations of
C++, doesn&#8217;t require special language extensions, and is relatively
bulletproof.</FONT><A NAME="_Toc305593291"></A><A NAME="_Toc305628763"></A><A NAME="_Toc312374106"></A><A NAME="_Toc519042111"></A><BR></P></DIV>
<A NAME="Heading320"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
Avoiding MI</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The need for multiple
inheritance<A NAME="Index638"></A> in <B>Persist2.cpp</B> is contrived, based on
the concept that you don&#8217;t have control of some of the code in the
project. Upon examination of the example, you can see that MI can be easily
avoided by using member objects of type <B>Data</B>, and putting the virtual
<B>read(&#160;)</B>and <B>write(&#160;)</B> members inside <B>Data</B> or
<B>WData1</B> and <B>WData2 </B>rather than in a separate class. There are many
situations like this one where multiple inheritance may be avoided; the language
feature is included for unusual, special-case situations that would otherwise be
difficult or impossible to handle. But when the question of whether to use
multiple inheritance comes up, you should ask two questions:</FONT><BR></P></DIV>
<OL>
<LI><FONT FACE="Verdana">	</FONT><FONT FACE="Georgia">Do I need to show the
public interfaces of both these classes, or could one class be embedded with
some of its interface produced with member functions in the new
class?</FONT><LI><FONT FACE="Verdana">	</FONT><FONT FACE="Georgia">Do I need to
upcast to both of the base classes? (This applies when you have more than two
base classes, of course.) </FONT></OL><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">If
you can&#8217;t answer &#8220;no&#8221; to both questions, you can avoid using
MI and should probably do so.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">One situation to watch for is when one
class only needs to be upcast as a function argument. In that case, the class
can be embedded and an automatic type conversion operator provided in your new
class to produce a reference to the embedded object. Any time you use an object
of your new class as an argument to a function that expects the embedded object,
the type conversion operator is used. However, type conversion can&#8217;t be
used for normal member selection; that requires
inheritance.</FONT><A NAME="_Toc305593292"></A><A NAME="_Toc305628764"></A><A NAME="_Toc312374107"></A><A NAME="_Toc519042112"></A><BR></P></DIV>
<A NAME="Heading321"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
Mixin types</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Rodents &amp; pets(play)</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">interfaces in
general</FONT><A NAME="_Toc519042113"></A><BR></P></DIV>
<A NAME="Heading322"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
Repairing an interface<BR><A NAME="Index639"></A><A NAME="Index640"></A></H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">One of the best arguments for multiple
inheritance involves code that&#8217;s out of your control. Suppose you&#8217;ve
acquired a library that consists of a header file and compiled member functions,
but no source code for member functions. This library is a class hierarchy with
virtual functions, and it contains some global functions that take pointers to
the base class of the library; that is, it uses the library objects
polymorphically. Now suppose you build an application around this library, and
write your own code that uses the base class polymorphically.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Later in the development of the project
or sometime during its maintenance, you discover that the base-class interface
provided by the vendor is incomplete: A function may be nonvirtual and you need
it to be virtual, or a virtual function is completely missing in the interface,
but essential to the solution of your problem. If you had the source code, you
could go back and put it in. But you don&#8217;t, and you have a lot of existing
code that depends on the original interface. Here, multiple inheritance is the
perfect solution.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">For example, here&#8217;s the header file
for a library you acquire:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C10:Vendor.h</font>
<font color=#009900>// Vendor-supplied class header</font>
<font color=#009900>// You only get this &amp; the compiled Vendor.obj</font>
#ifndef VENDOR_H
#define VENDOR_H

<font color=#0000ff>class</font> Vendor {
<font color=#0000ff>public</font>:
  <font color=#0000ff>virtual</font> <font color=#0000ff>void</font> v() <font color=#0000ff>const</font>;
  <font color=#0000ff>void</font> f() <font color=#0000ff>const</font>;
  ~Vendor();
};

<font color=#0000ff>class</font> Vendor1 : <font color=#0000ff>public</font> Vendor {
<font color=#0000ff>public</font>:
  <font color=#0000ff>void</font> v() <font color=#0000ff>const</font>;
  <font color=#0000ff>void</font> f() <font color=#0000ff>const</font>;
  ~Vendor1();
};

<font color=#0000ff>void</font> A(<font color=#0000ff>const</font> Vendor&amp;);
<font color=#0000ff>void</font> B(<font color=#0000ff>const</font> Vendor&amp;);
<font color=#009900>// Etc.</font>
#endif <font color=#009900>// VENDOR_H ///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Assume the library is much bigger, with
more derived classes and a larger interface. Notice that it also includes the
functions <B>A(&#160;)</B> and <B>B(&#160;)</B>, which take a base pointer and
treat it polymorphically. Here&#8217;s the implementation file for the
library:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C10:Vendor.cpp {O}</font>
<font color=#009900>// Implementation of VENDOR.H</font>
<font color=#009900>// This is compiled and unavailable to you</font>
#include <font color=#004488>"Vendor.h"</font>
#include &lt;fstream&gt;
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;

<font color=#0000ff>extern</font> ofstream out; <font color=#009900>// For trace info</font>

<font color=#0000ff>void</font> Vendor::v() <font color=#0000ff>const</font> {
  out &lt;&lt; <font color=#004488>"Vendor::v()\n"</font>;
}

<font color=#0000ff>void</font> Vendor::f() <font color=#0000ff>const</font> {
  out &lt;&lt; <font color=#004488>"Vendor::f()\n"</font>;
}

Vendor::~Vendor() {
  out &lt;&lt; <font color=#004488>"~Vendor()\n"</font>;
}

<font color=#0000ff>void</font> Vendor1::v() <font color=#0000ff>const</font> {
  out &lt;&lt; <font color=#004488>"Vendor1::v()\n"</font>;
}

<font color=#0000ff>void</font> Vendor1::f() <font color=#0000ff>const</font> {
  out &lt;&lt; <font color=#004488>"Vendor1::f()\n"</font>;
}

Vendor1::~Vendor1() {
  out &lt;&lt; <font color=#004488>"~Vendor1()\n"</font>;
}

<font color=#0000ff>void</font> A(<font color=#0000ff>const</font> Vendor&amp; V) {
  <font color=#009900>// ...</font>
  V.v();
  V.f();
  <font color=#009900>//..</font>
}

<font color=#0000ff>void</font> B(<font color=#0000ff>const</font> Vendor&amp; V) {
  <font color=#009900>// ...</font>
  V.v();
  V.f();
  <font color=#009900>//..</font>
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In your project, this source code is
unavailable to you. Instead, you get a compiled file as <B>Vendor.obj</B> or
<B>Vendor.lib</B> (or the equivalent for your system).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The problem occurs in the use of this
library. First, the destructor isn&#8217;t virtual. This is actually a design
error on the part of the library creator. In addition, <B>f(&#160;)</B> was not
made virtual; assume the library creator decided it wouldn&#8217;t need to be.
And you discover that the interface to the base class is missing a function
essential to the solution of your problem. Also suppose you&#8217;ve already
written a fair amount of code using the existing interface (not to mention the
functions <B>A(&#160;)</B> and <B>B(&#160;),</B> which are out of your control),
and you don&#8217;t want to change it.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To repair the problem, create your own
class interface and multiply inherit a new set of derived classes from your
interface and from the existing classes:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C10:Paste.cpp</font>
<font color=#009900>// Fixing a mess with MI</font>
<font color=#009900>//{L} Vendor ../TestSuite/Test</font>
#include <font color=#004488>"Vendor.h"</font>
#include &lt;fstream&gt;
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;

ofstream out(<font color=#004488>"paste.out"</font>);

<font color=#0000ff>class</font> MyBase { <font color=#009900>// Repair Vendor interface</font>
<font color=#0000ff>public</font>:
  <font color=#0000ff>virtual</font> <font color=#0000ff>void</font> v() <font color=#0000ff>const</font> = 0;
  <font color=#0000ff>virtual</font> <font color=#0000ff>void</font> f() <font color=#0000ff>const</font> = 0;
  <font color=#009900>// New interface function:</font>
  <font color=#0000ff>virtual</font> <font color=#0000ff>void</font> g() <font color=#0000ff>const</font> = 0;
  <font color=#0000ff>virtual</font> ~MyBase() { out &lt;&lt; <font color=#004488>"~MyBase()\n"</font>; }
};

<font color=#0000ff>class</font> Paste1 : <font color=#0000ff>public</font> MyBase, <font color=#0000ff>public</font> Vendor1 {
<font color=#0000ff>public</font>:
  <font color=#0000ff>void</font> v() <font color=#0000ff>const</font> {
    out &lt;&lt; <font color=#004488>"Paste1::v()\n"</font>;
    Vendor1::v();
  }
  <font color=#0000ff>void</font> f() <font color=#0000ff>const</font> {
    out &lt;&

⌨️ 快捷键说明

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