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

📄 apa.htm

📁 A very good resource on Visual C++ 6.0 environment. It teaches through step by step approach and fin
💻 HTM
📖 第 1 页 / 共 3 页
字号:
A pointer that points to a derived class object is being carried around as a pointer
to a base class object. Although there doesn't seem to be any use at all in doing
such a thing in this code example, it would be very useful to be able to make an
array of BankAccount pointers and then pass it to a function that wouldn't have to
know that SavingsAccount or CheckingAccount objects even existed. You will see an
example of that in a moment, but first you need to be clear about what's happening
in Listing A.8.</P>
<P>The call to pb-&gt;Display() will execute the BankAccount::Display() function,
not surprisingly. The call to pc-&gt;Display() would execute CheckingAccount::Display()
if you had written one, but because CheckingAccount only inherits the base class
code, this call will be to BankAccount::Display(), also. The call to ps-&gt;Display()
will execute the override, SavingsAccount::Display(). This is exactly the behavior
you want. Each account will be displayed completely and properly.</P>
<P>Things aren't as simple when it comes to pc2 and ps2, however. These pointers,
though they point to a CheckingAccount object and a SavingsAccount object, are declared
to be of type pointer-to-BankAccount. Each of the display calls will execute the
BankAccount::Display() function, which is not what you want at all. To achieve the
desired behavior, you must include the keyword virtual in your declaration of BankAccount::Display().
(The keyword must appear in the base class declaration of the function.) When you
do so, you are asking for polymorphism, asking that the same line of code sometimes
do quite different things. To see how this can happen, consider a function such as
this one:</P>
<P>
<PRE>void SomeClass::DisplayAccounts(BankAccount* a[], int numaccounts)
{
   for (int i = 0; i &lt; numaccounts; i++)
   {
       a[i]-&gt;Display();
   }
}
</PRE>
<P>This function takes an array of BankAccount pointers, goes through the array,
and displays each account. If, for example, the first pointer is pointing to a CheckingAccount
object, BankAccount::Display() will be executed. If the second pointer is pointing
to a SavingsAccount object, and Display() is virtual, SavingsAccount::Display() will
be executed. You can't tell by looking at the code which lines will be executed,
and that's polymorphism.</P>
<P>It's a tremendously useful feature. Without it, you'd have to write switch statements
that decide which function to call, and every time you add another kind of BankAccount,
you'd have to find those switch statements and change them. With it, you can add
as many new kinds of BankAccount classes as you want, and you never have to change
SomeClass::DisplayAccounts() to accommodate that.</P>
<P>
<H2><A NAME="Heading13"></A>Managing Memory</H2>
<P>When you declare an object in a block of code, it lasts only until the last line
of code in the block has been executed. Then the object goes out of scope, and its
memory is reclaimed. If you want some cleanup task taken care of, you write a <I>destructor
</I>(the opposite of a constructor) for the object, and the system will call the
destructor before reclaiming the memory.</P>
<P>Often you want to create an object that will continue to exist past the lifetime
of the function that created it. You must, of course, keep a pointer to the object
somewhere. A very common situation is to have the pointer as a member variable of
a class: The constructor for the class allocates the memory, and the destructor releases
it.</P>
<P>
<H3><A NAME="Heading14"></A>Allocating and Releasing Memory</H3>
<P>In C, you allocate memory like this with the malloc() function. For example, to
allocate enough memory for a single integer, you would write</P>
<P>
<PRE>int *pi = (int *) malloc ( sizeof(int) );
</PRE>
<P>However, when you allocate memory for an object, you want the constructor to run.
malloc(), written long before C++ was developed, can't call constructors. Therefore,
you use an operator called new to allocate and initialize the memory, like this:</P>
<P>
<PRE>BankAccount* pb = new BankAccount(&quot;AB123456&quot;,&quot;11038-30&quot;,100.00);
</PRE>
<P>The parameters after the classname are passed along to the constructor, as they
were when you allocated a BankAccount within a block. Not only does new call the
constructor, but you also don't have to calculate the number of bytes you need with
sizeof, and you don't have to cast the pointer you receive back. This is a handy
operator.</P>


<BLOCKQUOTE>
	<P>
<HR>
<STRONG>TIP:</STRONG> The place where this memory is allocated is technically called
	the<I> free store</I>. Many C++ developers call it the<I> heap</I>. On the other
	hand, variables allocated within a block are said to be <I>on the stack</I>. 
<HR>


</BLOCKQUOTE>

<P>When you're finished with the object you allocated with new, you use the delete
operator to get rid of it, like this:</P>
<P>
<PRE>delete pb;
</PRE>
<P>delete will call the destructor and then reclaim the memory. The older C function,
free(), must never be used to release memory that was allocated with new. If you
allocate some other memory (say, a dynamic array of integers) with malloc(), you
must release it with free() rather than delete. Many developers find it simpler to
leave free() and malloc() behind forever and use new and delete exclusively.</P>
<P>new can be used for array allocation, like this:</P>
<P>
<PRE>int * numbers = new int[100];
</PRE>
<P>When you are finished with memory that was allocated like this, always use the
array form of delete to release it:</P>
<P>
<PRE>delete[] numbers;
</PRE>
<H3><A NAME="Heading15"></A>Pointers as Member Variables</H3>
<P>It's common to use pointers within objects. Consider the BankAccount class that's
been the example throughout this chapter. Why should it carry around a character
string representing a customer identifier? Wouldn't it be better to carry around
a pointer to an object that is an instance of the class Customer? This is easy to
do. Remove the private customer_id variable and add a Customer pointer to the class
declaration, like this:</P>
<P>
<PRE>Customer* pCustomer;
</PRE>
<P>You would have to write code in the constructor that finds the right Customer
object using only the customer identifer passed to the constructor, and you would
probably add two new constructors that take Customer pointers. You can't take away
a public function after you've written it, because someone might be relying on it.
If you are sure no one is, you could remove it.</P>
<P>Now a BankAccount can do all sorts of useful things by delegating to the Customer
object it is associated with. Need to print the customer's name and address at the
top of the statement? No problem, have the Customer object do it:</P>
<P>
<PRE>pCustomer-&gt;PrintNameandAddress();
</PRE>
<P>This is a terrific way to reuse all the work that went into formatting the name
and address in the Customer class. It also completely isolates you from changes in
that format later.</P>


<BLOCKQUOTE>
	<P>
<HR>
<STRONG>TIP:</STRONG> This kind of reuse is generally called <I>aggregation</I> or
	<I>containment</I> and is contrasted with inheritance. It corresponds to the HAS
	sentences presented in the inheritance section. 
<HR>


</BLOCKQUOTE>

<H3><A NAME="Heading16"></A>Dynamic Objects</H3>
<P>A BankAccount is always associated with exactly one Customer object. However,
there are other things about a BankAccount that might or might not exist. Perhaps
an account is associated with a CreditCard, and if so, possible overdrafts are covered
from that card.</P>
<P>To implement this in code, you would add another private member variable to the
BankAccount class:</P>
<P>
<PRE>CreditCard *pCard;
</PRE>
<P>All the constructors written so far would set this pointer to NULL to indicate
that it doesn't point to a valid CreditCard:</P>
<P>
<PRE>pCard = NULL;
</PRE>
<P>You could then add a public function such as AddCreditCard() that would set the
pointer. The code could be inline, like this:</P>
<P>
<PRE>void AddCreditCard(CreditCard* card) {pCard = card;}
</PRE>
<P>The new code for Withdraw() would probably look like this:</P>
<P>
<PRE>void BankAccount::Withdraw(float amounttowithdraw)
 {
     balance -= amounttowithdraw;
     if (balance &lt; 0)
        {
           if (pCard)
           {
              int hundreds = - (int) (balance / 100); 
              hundreds++;
              pCard-&gt;CashAdvance(hundreds * 100);
              balance += hundreds * 100;
           }
           else
              balance += amounttowithdraw; //reverse transaction
      }
 }
</PRE>
<P>This rounds the overdraft (not the withdrawal) to the nearest hundred and obtains
that amount from the credit card. If this account has no associated card, it reverses
the transaction, as before.</P>
<P>
<H3><A NAME="Heading17"></A>Destructors and Pointers</H3>
<P>When a BankAccount object is thrown away, the Customer or CreditCard objects to
which it might have had pointers continue to exist. That means BankAccount doesn't
need a destructor at the moment. Many times, objects with pointers as member variables
do need destructors.</P>
<P>Consider the situation of ordering new checks. When the checks arrive, the charge
is taken out of the account. Perhaps you will make a CheckOrder object to gather
up the information and will add a function to CheckingAccount to make one of these.
Without taking this example too far afield by trying to design CheckOrder, the OrderChecks()
function might look like this:</P>
<P>
<PRE>CheckingAccount::OrderChecks()
{
   pOrder = new CheckOrder( /* whatever parameters the constructor takes */);
}   
</PRE>
<P>You would add pOrder as a private member variable of CheckingAccount:</P>
<P>
<PRE>   CheckOrder* pOrder; 
</PRE>
<P>In the constructor for CheckingAccount, you would set pOrder to NULL because a
brand new account doesn't have an outstanding check order.</P>
<P>When the checks arrive, whatever outside code called OrderChecks() could call
ChecksArrive(), which would look like this:</P>
<P>
<PRE>CheckingAccount::ChecksArrive()
{
    balance -= pOrder.GetCharge();
    delete pOrder;
    pOrder = NULL;
}   
</PRE>


<BLOCKQUOTE>
	<P>
<HR>
<STRONG>NOTE:</STRONG> This function will be able to access balance directly like
	this only if balance was protected in BankAccount rather than private, as discussed
	earlier.&#160;n 
<HR>


</BLOCKQUOTE>

<P>The delete operator will clean up the order object by running its destructor and
then reclaim the memory. You set the pointer to NULL afterwards to make sure that
no other code tries to use the pointer, which no longer points to a valid CheckOrder
object.</P>
<P>What if a CheckingAccount is closed while an order is outstanding? If you throw
away the pointer, the memory occupied by the CheckOrder object will never be reclaimed.
You will have to write a destructor for CheckingAccount that cleans this up. Remember
that constructors always have the same name as the class. Destructor names are always
a tilde (~) followed by the name of the class. CheckingAccount::~CheckingAccount()
would look like this:</P>
<P>
<PRE>CheckingAccount::~CheckingAccount()
{
    delete pOrder;
}   
</PRE>
<H3><A NAME="Heading18"></A>Running Destructors Accidentally</H3>
<P>When a class has a destructor that does something destructive, you have to be
very careful to make sure that it isn't unexpectedly called and causes you trouble.
Look at the code in Listing A.9. It makes a CheckingAccount object, orders checks,
passes the object to some function or another, and then tells the account that the
checks have arrived.</P>
<P>
<H4>Listing A.9&#160;&#160;Accidental Destruction</H4>
<PRE>CheckingAccount ca(&quot;AB123456&quot;,&quot;11038-32&quot;,200.00);
ca.OrderChecks();
SomeFunction(ca);
</PRE>
<PRE>ca.ChecksArrive();
</PRE>
<P>This looks harmless enough. However, when you pass the CheckingAccount object
to SomeFunction(), the system makes a copy of it to give to the function. This copy
is identical to ca: It has a pointer in it that points to the same CheckOrder as
ca. When the call to SomeFunction() returns, the copy is no longer needed, so the
system runs the destructor and reclaims the memory. Unfortunately, the destructor
for the temporary CheckingAccount object will delete its CheckOrder, which is also
ca's CheckOrder. The call to ChecksArrive() can't work because the CheckOrder object
is gone.</P>
<P>There are two ways to deal with this problem. The first is to change SomeFunction()
so that it takes a pointer to a CheckingAccount or a reference to a CheckingAccount.
The second is to write a function called a <I>copy</I> <I>constructor</I> that controls
the way the temporary CheckingAccount is made. References and copy constructors are
beyond the scope of this chapter. If the function takes a pointer, no copy is made,
and there can be no accidental destruction.</P>
<P>
<H3><A NAME="Heading19"></A>What Else Should I Know?</H3>
<P>If you bought a book solely on C++ or attended a week-long introductory course,
you would learn a number of other C++ features, including the following:</P>

<UL>
	<LI>Default parameter values
	<P>
	<LI>Constructor initializer line
	<P>
	<LI>The const keyword
	<P>
	<LI>Passing parameters by reference
	<P>
	<LI>Returning values by reference
	<P>
	<LI>Static member variables and static member functions
	<P>
	<LI>Copy constructors
	<P>
	<LI>Operator overloading
</UL>

<P>Two topics not always covered in introductory material are exceptions and templates.
These are discussed in Chapter 26, &quot;Exceptions and Templates.&quot;</P>
<H1></H1>
<CENTER>
<P>
<HR>
<A HREF="ch28.htm" tppabs="http://www.fintech.ru/library/prog/SEUsingVC6/ch28/ch28.htm"><IMG SRC="previous.gif" tppabs="http://www.fintech.ru/library/prog/SEUsingVC6/button/previous.gif" WIDTH="128" HEIGHT="28"
ALIGN="BOTTOM" ALT="Previous chapter" BORDER="0"></A><A HREF="apb.htm" tppabs="http://www.fintech.ru/library/prog/SEUsingVC6/apb/apb.htm"><IMG
SRC="next.gif" tppabs="http://www.fintech.ru/library/prog/SEUsingVC6/button/next.gif" WIDTH="128" HEIGHT="28" ALIGN="BOTTOM" ALT="Next chapter"
BORDER="0"></A><A HREF="index.htm" tppabs="http://www.fintech.ru/library/prog/SEUsingVC6/index.htm"><IMG SRC="contents.gif" tppabs="http://www.fintech.ru/library/prog/SEUsingVC6/button/contents.gif" WIDTH="128"
HEIGHT="28" ALIGN="BOTTOM" ALT="Contents" BORDER="0"></A> <BR>
</P>

<P>&#169; <A HREF="copy.htm" tppabs="http://www.fintech.ru/library/prog/SEUsingVC6/copy.htm">Copyright</A>, Macmillan Computer Publishing. All
rights reserved.
</CENTER>


</BODY>

</HTML>

⌨️ 快捷键说明

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