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

📄 chapter 10 virtual methods.htm

📁 英文版编译器设计:里面详细介绍啦C编译器的设计
💻 HTM
📖 第 1 页 / 共 3 页
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!-- saved from url=(0063)http://topaz.cs.byu.edu/text/html/Textbook/Chapter10/index.html -->
<HTML><HEAD><TITLE>Chapter 10: Virtual Methods</TITLE>
<META http-equiv=Content-Type content="text/html; charset=iso-8859-1">
<META content="MSHTML 6.00.2800.1458" name=GENERATOR></HEAD>
<BODY>
<CENTER>
<H1>Chapter 10<BR>Virtual Methods</H1></CENTER>
<HR>
<!-------------------------------------------------------------------------------->
<H2>10.1 What are Virtual Methods?</H2><!-------------------------------------------------------------------------------->Virtual 
methods are one of the central features of object oriented programming. They 
allow programmers to manipulate groups of objects through a standard interface. 
Consider a class, <PRE>      type
        Shape is class
          virtual proc WhoAmI;
          begin
            write "I am some general shape...\n";
          end proc;
        end class;

      <B>Listing {CLSSHP}.</B>  A class with one virtual method.
</PRE>Notice the presence of the keyword <TT>virtual</TT> in front of the 
declaration of the procedure method, <TT>WhoAmI()</TT>. This keyword tells the 
compiler that other subclasses will have their own inplementations of function 
<TT>WhoAmI()</TT> that should be used, instead.
<P>We can make an instance of this class, but it wouldn't do much. This class 
was meant to be a virtual base class. There is nothing really special about such 
a class except in the way that it is <I>used</I>. The idea is that we declare 
such a class and use it as a standard way of manipulating other classes that 
will inherit from it.
<P>Suppose we have three other classes as shown in listing {CLSSHPS}. <PRE>      type
        Circle is class
          extends Shape;

          proc WhoAmI;
          begin
            write "I am a Circle.\n";
          end proc;
        end class;

        Square is class
          extends Shape;

          proc WhoAmI;
          begin
            write "I am a Square.\n";
          end proc;
        end class;

        Triangle is class
          extends Shape;

          proc WhoAmI;
          begin
            write "I am a Triangle.\n";
          end proc;
        end class;

      <B>Listing {CLSSHP}.</B>  Three classes, <TT>Circle</TT>, <TT>Square</TT>, and <TT>Triangle</TT> 
      inherit from a general base class, <TT>Shape</TT>.  Using a pointer to <TT>Shape</TT>, we 
      can manipulate any of these other classes.
</PRE>There are now three classes that are more useful, and all of them can be 
controlled through their <TT>Shape</TT> base class. We can do many things with 
such a concept. We can make an abstract data type like a stack or a list or a 
tree that stores <TT>Shape</TT> instances, and we can store anything that 
inherits from a <TT>Shape</TT> superclass.
<P>The way to make use of a group of classes that have a common virtual base 
class is to make a pointer to the common base class. suppose we had a function 
that iterated through a list of shapes and returned a pointer to a 
<TT>Shape</TT>. <PRE>      func GetNext: ^Shape;
</PRE>Every time we call <TT>GetNext()</TT> we will get the next shape in the 
list, until there are no more. With each shape, we can tell it to tell us who it 
is. <PRE>      var
         pshape: ^shape;
      begin
        pshape:= GetNext();
        while pshape &lt;&gt; null do
          pshape^.WhoAmI();
          pshape:= GetNext();
        loop;
      end
</PRE>One of the principles central to object oriented programming is that we do 
away with building separate cases for all different types of things. Instead we 
make each thing become a class that is controlled through a standard interface, 
and tell each class, "Go and do that thing that you're supposed to do instead, 
when this method of your base class is called." This is commonly called message 
passing.
<P>Under SAL, virtual methods are only used under the following circumstances: 
<OL>
  <LI>A class has any method, <TT>x()</TT> that is declared with the 
  <TT>virtual</TT> keyword. 
  <LI>A deriving class (either direct or indirect) has a maching method whose 
  declaration is identical to <TT>x()</TT>. Parameter list and return type must 
  be the same. 
  <LI>The method <TT>x()</TT> is called through a pointer or a reference to any 
  instance inheriting (either directly or indirectly) from the base class 
  containing the original definition for <TT>x()</TT>. </LI></OL>
<H3>10.1.1 Virtual Methods and Shadowing</H3><!-------------------------------------------------------------------------------->There 
is a subtle difference between virtual methods and shadowing. One is from the 
point of view of the base class, and the other is from the point of view of the 
derived class. Shadowing occurrs when we have an instance of a derived class 
that is a variable, i.e., not a pointer or a reference. To demonstrate 
shadowing, suppose we have two variables, a, and b, like so: of <TT>Shape</TT> 
like so, <PRE>      var
        a: Shape;
        b: Square;
</PRE>When we call <TT>a.WhoAmI()</TT>, we get a message that says <PRE>      I am some general shape...
</PRE>and when call <TT>b.WhoAmI()</TT>, we would get a message that says <PRE>      I am a Square.
</PRE>In the case of the instance of <TT>Square</TT> the call to 
<TT>Shape::WhoAmI()</TT> was shadowed by <TT>Square::WhoAmI()</TT>.
<P>The virtual method concept makes sure that this behavior still holds when all 
we have is a pointer to a <TT>Shape</TT>. The actual instance may be a piece of 
a <TT>Circle</TT> or a <TT>Triangle</TT> or any other class that inherits from 
<TT>Shape</TT>, but it will be all the same to us, because all we really need to 
manipulate each object is the standard interface that we have defined.
<P><!-------------------------------------------------------------------------------->
<H2>10.2 How Virtual Methods Work</H2><!-------------------------------------------------------------------------------->Virtual 
methods work by making use of virtual tables, or vtables (V-tables) for short, 
and another mechanism called virtual indexing. Since a compiler is oblidged to 
compile a base class that will be made use of at a later date, virtual methods 
are only something that can be resolved at runtime. A vtable is a runtime 
component that tells a program what function it should call instead of the one 
that the author originally stated. All classes in SAL contain a pointer to a 
vtable. If the class has no virtual methods, then the pointer is set to 
<TT>null</TT>. The vtable pointer is always the first four bytes of every class 
instance.
<P>
<CENTER><IMG height=230 src="Chapter 10 Virtual Methods.files/GNRVFT.gif" 
width=640></CENTER>In SAL each vtable has a four-byte marker to identify its 
contents in memory. The bytes are ascii text, and are the letters "VTBL". 
Following this marker is the vtable data. Each class's vtable pointer points 
directly to this data. The data consists of a list of tuples, where the first 
element of each tuple is a pointer to the actual function to be called, and the 
second element is a delta to be added to the pointer to self.
<P>
<H3>10.2.1 Virtual Methods in Single Inheritance</H3><!-------------------------------------------------------------------------------->In 
addition to the vtable, each virtual function belonging to a class has its own 
index into the virtual table. Let's consider a new class with a couple of 
virtual methods and some member variables: <PRE>      type
        C is class
          i, j, k: int;

          virtual proc foo;
          begin
            write "I am C::foo().\n";
          enc proc;

          virtual proc bar;
          begin
            write "I am C::bar().\n";
          enc proc;
        end class;

      <B>Listing {CLSVC}.</B>  A base class with two virtual functions.
</PRE>Since class <TT>C</TT> has no superclasses, the indicies for its virtual 
functions begins at zero (or they can begin at one depending on the base of the 
count). Method <TT>foo()</TT> is declared, and so its virtual index is zero. 
Method <TT>bar()</TT> comes next, and its index is one. If class <TT>C</TT> had 
more virtual methods, then the indexing would proceed to two, and then three, 
and so on until all methods that were virtual had been indexed. A memory layout 
for class <TT>C</TT> would look like figure {MEMVC} 
<CENTER><IMG src="Chapter 10 Virtual Methods.files/MEMVC.gif"></CENTER>With 
single inheritance, each subclass shares a vtable pointer with the super-most 
class. In listing {CLSVBA}, classes <TT>B</TT> and <TT>A</TT> subclass class 
<TT>C</TT>. Class <TT>B</TT> has two virtual methods. One is virtual by the fact 
that it matches a method in class <TT>C</TT>, and the other is declared virtual. 
Class <TT>A</TT> also has two virtual methods, and overrides one of the methods 
in <TT>B</TT> and one of the methods of <TT>C</TT>. <PRE>      type
        B is class
          extends C;

          a, b, c: int;

          proc bar;
          begin
            write "I am B::bar().\n";
          enc proc;

          virtual proc baz;
          begin
            write "I am B::baz().\n";
          enc proc;
        end class;

        A is class
          extends B;

          p, q, r: int;

          proc foo;
          begin
            write "I am A::foo().\n";
          enc proc;

          proc baz;
          begin
            write "I am A::baz().\n";
          enc proc;
        end class;



      <B>Listing {CLSVBA}.</B>  Class B extends C, and overrides method C::bar().
      B also has a virtual method called baz().  Class A overrides C::foo() and
      B::baz().
</PRE>Since class <TT>B</TT> inherits from <TT>C</TT>, it continues indexing 
where <TT>C</TT> left off. Thus, <TT>B</TT>'s <TT>bar()</TT> is numbered 2, and 
<TT>baz()</TT> gets numbered 3. In a similar manner, when class <TT>A</TT> 
inherits <TT>B</TT>, it continues numbering where <TT>B</TT> leaves off. Its 
<TT>foo()</TT> is numbered 4, and its <TT>baz()</TT> is numbered 5. If class 
<TT>B</TT> had no virtual functions, then <TT>A</TT> would have began indexing 
where <TT>C</TT> left off, and <TT>B</TT> would have no entries in its own 
vtable and no entries in <TT>A</TT>'s vtable.
<P>With the vtables for classes <TT>B</TT> and <TT>A</TT>, something slightly 
different happens. For every method in class <TT>B</TT> that matches a virtual 
method in class <TT>C</TT>, the entries in class <TT>C</TT>'s portion of 
<TT>B</TT>'s vtable are altered so that <TT>B</TT>'s method gets called, 
instead. A similar thing happens for class <TT>A</TT>. It replaces methods for 
both classes <TT>C</TT> and <TT>B</TT>. Memory layouts for classes <TT>B</TT> 
and <TT>A</TT> whith their vtables are shown in figure {MEMVBA}.
<P>
<CENTER><IMG src="Chapter 10 Virtual Methods.files/MEMVBA.gif"></CENTER>In the 
diagram above, we use a certain notation for indexing the vtables. Although this 
notation is not used internally by the compiler, it is useful to see how the 
vtable is divided up. The notation is read as <PRE>      Classname1Index#.Classname2Index#.Classname3Index#....
</PRE>Where Classname1 is the containing class, and Classname2 and the rest are 
base classes. In figure {MEMVBA} the first item of the first vtable is indexed 
as B0.C0. This means that the first entry is class B's zeroth entry and class 
C's zeroth entry. This notation will become especially meaningful when we start 
to talk about multiple inheritance.
<P>Notice the change in the vtable for class <TT>B</TT> (Top part of figure 
{MEMVBA}, entry B1.C1. <TT>C::bar()</TT> has been substituted with 
<TT>B::bar()</TT>. A similar thing has happened in the vtable for class 
<TT>A</TT>; entries A0.B0.C0 and A3.B3 are different. <TT>C::foo()</TT> was 
replaced by <TT>A::foo()</TT>, and <TT>B::baz()</TT> was replaced by 
<TT>A::baz()</TT>.
<P>The vtable layouts for classes <TT>C</TT>, <TT>B</TT>, and <TT>A</TT> all 
contain zeros for the delta column. This is because the instances of these three 
classes all start at the same spots in memory. A pointer to an instance of class 
<TT>A</TT> also points to an instance of <TT>B</TT> and an instance of 
<TT>C</TT>. This is true for all pointers to instances of <TT>A</TT>.
<P>

⌨️ 快捷键说明

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