📄 tut4-4.html
字号:
<HTML>
<HEAD>
<TITLE>C++ Tutorial: 4.4, Polymorphism</TITLE>
<META NAME="description" CONTENT="pointers to base classes. Virtual members. Abstract base classes.">
<META NAME="keywords" CONTENT="this virtual class">
</HEAD>
<BODY BGCOLOR="white">
<!--captut-->
<CENTER>
<TABLE WIDTH=100% CELLPADDING=0 CELLSPACING=1 BORDER=0>
<TR><TD WIDTH=90%>
<FONT SIZE=4> Section 4.4 </FONT><BR>
<FONT SIZE=5><B>Polymorphism</B></FONT>
</TD><TD><!--ad--><!--#include virtual="/ad/ad468.shtml"--><!--/ad-->
</TD><TD VALIGN="bottom"><A HREF="http://www.cplusplus.com/doc/tutorial/">
<IMG SRC="head.gif" ALT="cplusplus.com" BORDER=0></A></TD></TR>
<TR><TD BGCOLOR="#0000FF" ALIGN="center" COLSPAN=3>
<IMG SRC="head0.gif" WIDTH=2 HEIGHT=2 BORDER=0></TD></TR>
</TABLE>
</CENTER>
<!--/captut-->
<P>
<CENTER>
<TABLE BORDER=1 CELLPADDING=5 WIDTH=90%><TR><TD>
For a suitable understanding of this section you should clearly know how to use
<B>pointers</B> and <B>inheritance between classes</B>. I recommend that if some
of these expressions seem strange to you, you review the indicated sections:
<TT><PRE>
int a::b(c) {}; <FONT COLOR="green"><I>// Classes (<A HREF="tut4-1.html#c">Section 4.1</A>)</I></FONT>
a->b <FONT COLOR="green"><I>// pointers and objects (<A HREF="tut4-2.html#a">Section 4.2</A>)</I></FONT>
class a: public b; <FONT COLOR="green"><I>// Relationships between classes (<A HREF="tut4-3.html#c">Section 4.3</A>)</I></FONT>
</PRE></TT>
</TD></TR></TABLE></CENTER>
<P>
<H2>Pointers to base class</H2>
One of the greater advantages of deriving classes is that <B><U>a pointer to a derived
class is type-compatible with a pointer to its base class</U></B>.
This section is fully dedicated to taking advantage of this powerful C++ feature.
For example, we are going to
rewrite our program about the rectangle and the triangle of the previous section
considering this property:
<P>
<CENTER>
<TABLE WIDTH=100% CELLPADDING=5 CELLSPACING=5><TR><TD BGCOLOR="#FFFFBF" WIDTH=50% VALIGN="top">
<TT><PRE><I>// pointers to base class</I>
#include <iostream.h>
class CPolygon {
protected:
int width, height;
public:
void set_values (int a, int b)
{ width=a; height=b; }
};
class CRectangle: public CPolygon {
public:
int area (void)
{ return (width * height); }
};
class CTriangle: public CPolygon {
public:
int area (void)
{ return (width * height / 2); }
};
int main () {
CRectangle rect;
CTriangle trgl;
CPolygon * ppoly1 = ▭
CPolygon * ppoly2 = &trgl;
ppoly1->set_values (4,5);
ppoly2->set_values (4,5);
cout << rect.area() << endl;
cout << trgl.area() << endl;
return 0;
}
</PRE></TT>
</TD><TD BGCOLOR="silver" WIDTH=50% VALIGN="top"><TT>
<B>20<BR>10</B>
</TT></TD></TR></TABLE>
</CENTER>
<P>
The function <TT><B>main</B></TT> creates two pointers that point to objects of class
<TT><B>CPolygon</B></TT>, that are <TT><B>*ppoly1</B></TT> and <TT><B>*ppoly2</B></TT>.
These are assigned to the addresses of <TT><B>rect</B></TT> and <TT><B>trgl</B></TT>,
and because they are objects of classes derived from <TT><B>CPolygon</B></TT>
they are valid assignations.
<P>
The only limitation of using <TT><B>*ppoly1</B></TT> and <TT><B>*ppoly2</B></TT>
instead of <TT><B>rect</B></TT> and <TT><B>trgl</B></TT>
is that both <TT><B>*ppoly1</B></TT> and <TT><B>*ppoly2</B></TT> are of type
<TT><B>CPolygon*</B></TT> and therefore we can only refer to the members that
<TT><B>CRectangle</B></TT> and <TT><B>CTriangle</B></TT> inherit from
<TT><B>CPolygon</B></TT>. For that reason when calling the <TT><B>area()</B></TT>
members we have not been able to use the pointers <TT><B>*ppoly1</B></TT> and
<TT><B>*ppoly2</B></TT>.
<P>
To make it possible for the pointers to class <TT><B>CPolygon</B><TT> to admit
<TT><B>area()</B></TT> as a valid member, this should also have been declared in the base class and
not only in its derived ones. (see the following section).
<P>
<H2>Virtual members</H2>
In order to declare an element of a class which we are going to redefine in derived
classes we must precede it with the keyword <TT><B>virtual</B></TT> so that the use
of pointers to objects of that class can be suitable.
<P>Take a look at the following example:
<P>
<CENTER>
<TABLE WIDTH=100% CELLPADDING=5 CELLSPACING=5><TR><TD BGCOLOR="#FFFFBF" WIDTH=50% VALIGN="top">
<TT><PRE><I>// virtual members</I>
#include <iostream.h>
class CPolygon {
protected:
int width, height;
public:
void set_values (int a, int b)
{ width=a; height=b; }
virtual int area (void)
{ return (0); }
};
class CRectangle: public CPolygon {
public:
int area (void)
{ return (width * height); }
};
class CTriangle: public CPolygon {
public:
int area (void)
{ return (width * height / 2); }
};
int main () {
CRectangle rect;
CTriangle trgl;
CPolygon poly;
CPolygon * ppoly1 = ▭
CPolygon * ppoly2 = &trgl;
CPolygon * ppoly3 = &poly;
ppoly1->set_values (4,5);
ppoly2->set_values (4,5);
ppoly3->set_values (4,5);
cout << ppoly1->area() << endl;
cout << ppoly2->area() << endl;
cout << ppoly3->area() << endl;
return 0;
}
</PRE></TT>
</TD><TD BGCOLOR="silver" WIDTH=50% VALIGN="top"><TT>
<B>20<BR>10<BR>0</B>
</TT></TD></TR></TABLE>
</CENTER>
<P>
Now the three classes (<TT><B>CPolygon</B></TT>, <TT><B>CRectangle</B></TT> and
<TT><B>CTriangle</B></TT>) have the same members: <TT><B>width</B></TT>,
<TT><B>height</B></TT>, <TT><B>set_values()</B></TT> and <TT><B>area()</B></TT>.
<P>
<TT><B>area()</B></TT> has been defined as <TT><B>virtual</B></TT> because it is later
redefined in derived classes. You can verify if you want that if you remove this word
(<TT><B>virtual</B></TT>) from the code and then you execute the program
the result will be <TT><B>0</B></TT> for the three polygons
instead of <TT><B>20,10,0</B></TT>. That is because instead of calling the
corresponding <TT><B>area()</B></TT> function for each object
(<TT><B>CRectangle::area()</B></TT>, <TT><B>CTriangle::area()</B></TT>
and <TT><B>CPolygon::area()</B></TT>, respectively), <TT><B>CPolygon::area()</B></TT> will
be called for all of them since the calls are via a pointer to <TT><B>CPolygon</B></TT>.
<P>
Therefore what the word <TT><B>virtual</B></TT> does is to allow that a member of a
derived class with the same name as one in the base class be suitably called when a
pointer to it is used, as in the above example.
<P>
Note that in spite of its virtuality we have also been able to declare an object of type
<TT><B>CPolygon</B></TT> and to call its <TT><B>area()</B></TT> function, that
always returns <TT><B>0</B></TT> as the result.
<P>
<H2>Abstract base classes</H2>
Basic abstract classes are something very similar to the class <TT><B>CPolygon</B></TT>
of our previous example. The only difference is that in our previous example we have
defined a valid <TT><B>area()</B></TT> function for objects that were of class
<TT><B>CPolygon</B></TT> (like object <TT><B>poly</B></TT>), whereas in an
<I>abstract base class</I> we could have simply left without defining this function
by appending <TT><B>=0</B></TT> (equal to zero) to the function declaration.
<P>
The class <TT><B>CPolygon</B></TT> could have been thus:
<BLOCKQUOTE><TT><PRE>
<I>// abstract class CPolygon</I>
class CPolygon {
protected:
int width, height;
public:
void set_values (int a, int b)
{ width=a; height=b; }
virtual int area (void) =0;
};
</PRE></TT></BLOCKQUOTE>
Notice how we have appended <TT><B>=0</B></TT> to <TT><B>virtual int area (void)</B></TT>
instead of specifying an implementation for the function. This type of function is called
a <I>pure virtual function</I>, and all classes that contain a
<I>pure virtual function</I> are considered <I>abstract base classes</I>.
<P>
The greatest difference of an abstract base class is that instances (objects) of it cannot
be created, but we can create pointers to them. Therefore a declaration like:
<blockquote ><TT><FONT COLOR="red">CPolygon poly;</FONT></TT></BLOCKQUOTE>
would be incorrect for the abstract base class declared above. Nevertheless the pointers:
<blockquote ><TT>CPolygon * ppoly1;<BR>CPolygon * ppoly2</TT></BLOCKQUOTE>
are be perfectly valid. This is because the <I>pure virtual function</I> that it
includes is not defined and it is impossible to create an object if it does not have
all its members defined. Nevertheless a pointer that points to an object of a derived
class where this function has been defined is perfectly valid.
<P>
Here you have the complete example:
<P>
<CENTER>
<TABLE WIDTH=100% CELLPADDING=5 CELLSPACING=5><TR><TD BGCOLOR="#FFFFBF" WIDTH=50% VALIGN="top">
<TT><PRE><I>// virtual members</I>
#include <iostream.h>
class CPolygon {
protected:
int width, height;
public:
void set_values (int a, int b)
{ width=a; height=b; }
virtual int area (void) =0;
};
class CRectangle: public CPolygon {
public:
int area (void)
{ return (width * height); }
};
class CTriangle: public CPolygon {
public:
int area (void)
{ return (width * height / 2); }
};
int main () {
CRectangle rect;
CTriangle trgl;
CPolygon * ppoly1 = ▭
CPolygon * ppoly2 = &trgl;
ppoly1->set_values (4,5);
ppoly2->set_values (4,5);
cout << ppoly1->area() << endl;
cout << ppoly2->area() << endl;
return 0;
}
</PRE></TT>
</TD><TD BGCOLOR="silver" WIDTH=50% VALIGN="top"><TT>
<B>20<BR>10</B>
</TT></TD></TR></TABLE>
</CENTER>
If you review the program you will notice that we can refer to objects of different
classes using a unique type of pointer (<TT><B>CPolygon*</B></TT>). This can be
tremendously useful. Imagine, now we can create a function member of
<TT><B>CPolygon</B></TT> that is able to print on screen the result of the
<TT><B>area()</B></TT> function independently of what the derived classes are.
<P>
<CENTER>
<TABLE WIDTH=100% CELLPADDING=5 CELLSPACING=5><TR><TD BGCOLOR="#FFFFBF" WIDTH=50% VALIGN="top">
<TT><PRE><I>// virtual members</I>
#include <iostream.h>
class CPolygon {
protected:
int width, height;
public:
void set_values (int a, int b)
{ width=a; height=b; }
virtual int area (void) =0;
void printarea (void)
{ cout << this->area() << endl; }
};
class CRectangle: public CPolygon {
public:
int area (void)
{ return (width * height); }
};
class CTriangle: public CPolygon {
public:
int area (void)
{ return (width * height / 2); }
};
int main () {
CRectangle rect;
CTriangle trgl;
CPolygon * ppoly1 = ▭
CPolygon * ppoly2 = &trgl;
ppoly1->set_values (4,5);
ppoly2->set_values (4,5);
ppoly1->printarea();
ppoly2->printarea();
return 0;
}
</PRE></TT>
</TD><TD BGCOLOR="silver" WIDTH=50% VALIGN="top"><TT>
<B>20<BR>10</B>
</TT></TD></TR></TABLE>
</CENTER>
<P>
Remember that <TT><B>this</B></TT> represents a pointer to the object
whose code is being executed.
<P>
Abstract classes and virtual members grant to C++ the polymorphic characteristics
that make object-oriented programming such a useful instrument. Of course we have
seen the simplest way to use these features, but imagine these features applied to
arrays of objects or objects assigned through dynamic memory.
<!--cuatut-->
<P>
<CENTER><TABLE WIDTH=100% CELLPADDING=0 CELLSPACING=0 BORDER=0>
<TR><TD BGCOLOR="#0000FF"><IMG SRC="head0.gif" WIDTH=2 HEIGHT=2></TD></TR>
<TR><TD ALIGN="right"><FONT FACE="arial,helvetica" SIZE=1>© The C++ Resources Network, 2000-2003 - All rights reserved</FONT></TD></TR>
</TABLE></CENTER>
<P>
<CENTER>
<TABLE CELLPADDING=0 WIDTH=100%>
<TR><TD ALIGN="right" WIDTH=45%><A HREF="tut4-3.html">
<IMG SRC="butnback.gif" ALIGN="right" BORDER=0>
Previous:<BR><B>4-3. Relationships between classes.</B></A></TD>
<TD ALIGN="center" WIDTH=10%><A HREF="index.html">
<IMG SRC="butnindx.gif" BORDER=0><BR>
index</A></TD>
<TD ALIGN="left" WIDTH=45%><A HREF="tut5-1.html">
<IMG SRC="butnnext.gif" ALIGN="left" BORDER=0>
Next:<BR><B>5-1. Templates.</B></A>
</TD></TR></TABLE>
</CENTER>
<!--/cuatut-->
</BODY>
</HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -