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

📄 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 页
字号:
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C10:Overhead.cpp</font>
<font color=#009900>// Virtual base class overhead</font>
<font color=#009900>//{L} ../TestSuite/Test</font>
#include &lt;fstream&gt;
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
ofstream out(<font color=#004488>"overhead.out"</font>);

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

<font color=#0000ff>class</font> NonVirtualInheritance
  : <font color=#0000ff>public</font> MBase {};

<font color=#0000ff>class</font> VirtualInheritance
  : <font color=#0000ff>virtual</font> <font color=#0000ff>public</font> MBase {};

<font color=#0000ff>class</font> VirtualInheritance2
  : <font color=#0000ff>virtual</font> <font color=#0000ff>public</font> MBase {};

<font color=#0000ff>class</font> MI
  : <font color=#0000ff>public</font> VirtualInheritance,
    <font color=#0000ff>public</font> VirtualInheritance2 {};

#define WRITE(ARG) \
out &lt;&lt; #ARG &lt;&lt; <font color=#004488>" = "</font> &lt;&lt; ARG &lt;&lt; endl;

<font color=#0000ff>int</font> main() {
  MBase b;
  WRITE(<font color=#0000ff>sizeof</font>(b));
  NonVirtualInheritance nonv_inheritance;
  WRITE(<font color=#0000ff>sizeof</font>(nonv_inheritance));
  VirtualInheritance v_inheritance;
  WRITE(<font color=#0000ff>sizeof</font>(v_inheritance));
  MI mi;
  WRITE(<font color=#0000ff>sizeof</font>(mi));
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Each of these classes only contains a
single byte, and the &#8220;core size&#8221; is that byte. Because all these
classes contain virtual functions, you expect the object size to be bigger than
the core size by a pointer (at least &#8211; your compiler may also pad extra
bytes into an object for alignment). The results are a bit surprising (these are
from one particular compiler; yours may do it differently):</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>sizeof</font>(b) = 2
<font color=#0000ff>sizeof</font>(nonv_inheritance) = 2
<font color=#0000ff>sizeof</font>(v_inheritance) = 6
<font color=#0000ff>sizeof</font>(MI) = 12</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Both <B>b</B> and
<B>nonv_inheritance</B> contain the extra pointer, as expected. But when virtual
inheritance is added, it would appear that the VPTR plus <I>two extra
pointers</I> are added! By the time the multiple inheritance is performed, the
object appears to contain five extra pointers (however, one of these is probably
a second VPTR for the second multiply inherited subobject).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The curious can certainly probe into your
particular implementation and look at the assembly language for member selection
to determine exactly what these extra bytes are for, and the cost of member
selection with multiple
inheritance</FONT><A NAME="fnB24" HREF="#fn24">[24]</A><A NAME="Index621"></A><FONT FACE="Georgia">.
The rest of you have probably seen enough to guess that quite a bit more goes on
with virtual multiple inheritance, so it should be used sparingly (or avoided)
when efficiency is an
issue.</FONT><A NAME="_Toc312374104"></A><A NAME="_Toc519042109"></A><BR></P></DIV>
<A NAME="Heading316"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
Upcasting<BR><A NAME="Index622"></A><A NAME="Index623"></A></H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When you embed subobjects of a class
inside a new class, whether you do it by creating member objects or through
inheritance, each subobject is placed within the new object by the compiler. Of
course, each subobject has its own <B>this</B> pointer, and as long as
you&#8217;re dealing with member objects, everything is quite straightforward.
But as soon as multiple inheritance is introduced, a funny thing occurs: An
object can have more than one <B>this</B> pointer because the object represents
more than one type during upcasting. The following example demonstrates this
point:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C10:Mithis.cpp</font>
<font color=#009900>// MI and the "this" pointer</font>
<font color=#009900>//{L} ../TestSuite/Test</font>
#include &lt;fstream&gt;
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
ofstream out(<font color=#004488>"mithis.out"</font>);

<font color=#0000ff>class</font> Base1 {
  <font color=#0000ff>char</font> c[0x10];
<font color=#0000ff>public</font>:
  <font color=#0000ff>void</font> printthis1() {
    out &lt;&lt; <font color=#004488>"Base1 this = "</font> &lt;&lt; <font color=#0000ff>this</font> &lt;&lt; endl;
  }
};

<font color=#0000ff>class</font> Base2 {
  <font color=#0000ff>char</font> c[0x10];
<font color=#0000ff>public</font>:
  <font color=#0000ff>void</font> printthis2() {
    out &lt;&lt; <font color=#004488>"Base2 this = "</font> &lt;&lt; <font color=#0000ff>this</font> &lt;&lt; endl;
  }
};

<font color=#0000ff>class</font> Member1 {
  <font color=#0000ff>char</font> c[0x10];
<font color=#0000ff>public</font>:
  <font color=#0000ff>void</font> printthism1() {
    out &lt;&lt; <font color=#004488>"Member1 this = "</font> &lt;&lt; <font color=#0000ff>this</font> &lt;&lt; endl;
  }
};

<font color=#0000ff>class</font> Member2 {
  <font color=#0000ff>char</font> c[0x10];
<font color=#0000ff>public</font>:
  <font color=#0000ff>void</font> printthism2() {
    out &lt;&lt; <font color=#004488>"Member2 this = "</font> &lt;&lt; <font color=#0000ff>this</font> &lt;&lt; endl;
  }
};

<font color=#0000ff>class</font> MI : <font color=#0000ff>public</font> Base1, <font color=#0000ff>public</font> Base2 {
  Member1 m1;
  Member2 m2;
<font color=#0000ff>public</font>:
  <font color=#0000ff>void</font> printthis() {
    out &lt;&lt; <font color=#004488>"MI this = "</font> &lt;&lt; <font color=#0000ff>this</font> &lt;&lt; endl;
    printthis1();
    printthis2();
    m1.printthism1();
    m2.printthism2();
  }
};

<font color=#0000ff>int</font> main() {
  MI mi;
  out &lt;&lt; <font color=#004488>"sizeof(mi) = "</font>
    &lt;&lt; hex &lt;&lt; <font color=#0000ff>sizeof</font>(mi) &lt;&lt; <font color=#004488>" hex"</font> &lt;&lt; endl;
  mi.printthis();
  <font color=#009900>// A second demonstration:</font>
  Base1* b1 = &amp;mi; <font color=#009900>// Upcast</font>
  Base2* b2 = &amp;mi; <font color=#009900>// Upcast</font>
  out &lt;&lt; <font color=#004488>"Base 1 pointer = "</font> &lt;&lt; b1 &lt;&lt; endl;
  out &lt;&lt; <font color=#004488>"Base 2 pointer = "</font> &lt;&lt; b2 &lt;&lt; endl;
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The arrays of bytes inside each class are
created with hexadecimal sizes, so the output addresses (which are printed in
hex) are easy to read. Each class has a function that prints its <B>this</B>
pointer, and these classes are assembled with both multiple inheritance and
composition into the class <B>MI,</B> which prints its own address and the
addresses of all the other subobjects. This function is called in
<B>main(&#160;)</B>. You can clearly see that you get two different <B>this</B>
pointers for the same object. The address of the <B>MI</B> object is taken and
upcast to the two different types. Here&#8217;s the
output:</FONT><A NAME="fnB25" HREF="#fn25">[25]</A><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>sizeof</font>(mi) = 40 hex
mi <font color=#0000ff>this</font> = 0x223e
Base1 <font color=#0000ff>this</font> = 0x223e
Base2 <font color=#0000ff>this</font> = 0x224e
Member1 <font color=#0000ff>this</font> = 0x225e
Member2 <font color=#0000ff>this</font> = 0x226e
Base 1 pointer = 0x223e
Base 2 pointer = 0x224e</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Although object layouts vary from
compiler to compiler and are not specified in Standard C++, this one is fairly
typical. The starting address of the object corresponds to the address of the
first class in the base-class list. Then the second inherited class is placed,
followed by the member objects in order of declaration.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When the upcast to the <B>Base1</B> and
<B>Base2</B> pointers occur, you can see that, even though they&#8217;re
ostensibly pointing to the same object, they must actually have different
<B>this</B> pointers, so the proper starting address can be passed to the member
functions of each subobject. The only way things can work correctly is if this
implicit upcasting takes place when you call a member function for a multiply
inherited
subobject.</FONT><A NAME="_Toc312374105"></A><A NAME="_Toc519042110"></A><BR></P></DIV>
<A NAME="Heading317"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H3 ALIGN="LEFT">
Persistence</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Normally this isn&#8217;t a problem,
because you want to call member functions that are concerned with that subobject
of the multiply inherited object. However, if your member function needs to know
the true starting address of the object, multiple inheritance causes problems.
Ironically, this happens in one of the situations where multiple inheritance
seems to be useful: <I>persistence</I>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The lifetime of a local object is the
scope in which it is defined. The lifetime of a global object is the lifetime of
the program. A <I>persistent object<A NAME="Index624"></A></I> lives between
invocations of a program: You can normally think of it as existing on disk
instead of in memory. One definition of an object-oriented
database<A NAME="Index625"></A><A NAME="Index626"></A> is &#8220;a collection of
persistent objects.&#8221;</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To implement persistence, you must move a
persistent object from disk into memory in order to call functions for it, and
later store it to disk before the program expires. Four issues arise when
storing an object on disk:</FONT><BR></P></DIV>
<OL>
<LI><FONT FACE="Verdana">	</FONT><FONT FACE="Georgia">The object must be
converted from its representation in memory to a series of bytes on
disk.</FONT><LI><FONT FACE="Verdana">	</FONT><FONT FACE="Georgia">Because the
values of any pointers in memory won&#8217;t have meaning the next time the
program is invoked, these pointers must be converted to something
meaningful.</FONT><LI><FONT FACE="Verdana">	</FONT><FONT FACE="Georgia">What the
pointers <I>point to</I> must also be stored and
retrieved.</FONT><LI><FONT FACE="Verdana">	</FONT><FONT FACE="Georgia">When
restoring an object from disk, the virtual pointers in the object must be
respected.</FONT></OL><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Because the object
must be converted back and forth between a layout in memory and a serial
representation on disk, the process is called <I>serialization</I>
<A NAME="Index627"></A>(to write an object to disk) and <I>deserialization</I>
<A NAME="Index628"></A>(to restore an object from disk). Although it would be
very convenient, these processes require too much overhead to support directly
in the language. Class libraries will often build in support for serialization
and deserialization by adding special member functions and placing requirements
on new classes. (Usually some sort of <B>serialize(&#160;)</B> function must be
written for each new class.) Also, persistence is generally not automatic; you
must usually explicitly write and read the objects.</FONT><BR></P></DIV>
<A NAME="Heading318"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H4 ALIGN="LEFT">
MI-based persistence</H4></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Consider sidestepping the pointer issues
for now and creating a class that installs persistence into simple objects using
multiple inheritance. By inheriting the <B>persistence</B> class along with your
new class, you automatically create classes that can be read from and written to
disk. Although this sounds great, the use of multiple inheritance introduces a
pitfall, as seen in the following example.</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C10:Persist1.cpp</font>
<font color=#009900>// Simple persistence with MI</font>
<font color=#009900>//{L} ../TestSuite/Test</font>
<font color=#009900>//{-g++3} dumps core</font>
#include <font color=#004488>"..</font><font color=#004488>/require.h"</font>
#include &lt;iostream&gt;
#include &lt;fstream&gt;

⌨️ 快捷键说明

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