home *** CD-ROM | disk | FTP | other *** search
/ C++ for Dummies (3rd Edition) / C_FD.iso / WORKOUT / PART5.TXT < prev    next >
Encoding:
Text File  |  1996-09-06  |  15.7 KB  |  512 lines

  1. 25-Minute Workout
  2. Part V Answers    
  3. 1
  4.     a.    The solution I chose was to give Savings its old display() function back. To do this, I 
  5. made the following additions (things that remained the same are not shown):
  6.     class Account
  7.     {
  8.        //everything else the same as before
  9.        virtual void display( );    //make this a virtual function
  10.     };
  11.  
  12.     //display - generic display function
  13.     //          (virtual fns canÆt be inline)
  14.     void Account::display( )
  15.     {
  16.           cout << ôAccount ô             << accountNumber
  17.                 << ô = ô                  << balance
  18.                << ô\nö;
  19.     }
  20.     
  21.     //Savings class with addition of display function
  22.     class Savings : public Account
  23.     {
  24.       public:
  25.        //everything else the same here, too
  26.        virtual void display( );    //add this declaration
  27.     };
  28.  
  29.     //display - unique display function for Savings
  30.     void Savings::display( )
  31.     {
  32.           cout  << ôAccount ô             << accountNumber
  33.                << ô = ô                  << balance
  34.                << ô (no. withdrawals = ô << noWithdrawals
  35.                << ô)\nö;
  36.     }
  37.         I declared Account::display() to be virtual so that I could overload it in the subclass 
  38. Savings. I then added the Savings::display() function from BUDGET4 to Savings. 
  39. Now, when main() displays the different accounts, the proper display() function is 
  40. chosen automatically, depending on the account type. The output of savings accounts 
  41. now appears with information on the number of withdrawals, as before:
  42.     Account totals:
  43.     Account 123 = 95 (no. withdrawals = 2)
  44.     Account 234 = 154.600006
  45.     Total worth  = 249.600006
  46.     b.    My class, NewSavings, appears as follows:
  47.     //NewSavings - implement a new savings account that charges
  48.     //             no withdrawal fee if balance is >= 750
  49.     class NewSavings : public Savings
  50.     {
  51.       public:
  52.        NewSavings(unsigned accNo, float initialBalance = 0.0) :
  53.              Savings(accNo, initialBalance) {}
  54.        //transaction functions++
  55.        virtual void withdrawal(float amount);
  56.     };
  57.  
  58.     void NewSavings::withdrawal(float amount)
  59.  
  60.     {
  61.        if (balance < amount)
  62.        {
  63.            cout << ôInsufficient funds: balance ô << balance
  64.                << ô, withdrawal ô                << amount
  65.                << ô\nö;
  66.        }
  67.         else
  68.         {
  69.            //if the balance is less than 750...
  70.            if (balance < 750.00F)
  71.             {
  72.                //...treat just like old savings account;
  73.             Savings::withdrawal(amount);   //Note 1
  74.            }
  75.           //...otherwise,...
  76.           else
  77.           {
  78.              //...no service charge (Yeah!)
  79.              balance -= amount;
  80.           }
  81.        }
  82.     }
  83.         Here, you see that once again the member function withdrawal() is overloaded by 
  84. one, which has the desired effect. Notice, however, that if the balance is below $750, 
  85. this new withdrawal() calls the old Savings::withdrawal() (Note 1). Remember that 
  86. including the name of the class in the call forces it to bind early even if the function is 
  87. declared virtual.
  88.     c.    Start with the separate list approach. This program does not differ much from the 
  89. BUDGET5 program presented earlier, but the changes are scattered over much of the 
  90. program. Therefore, to avoid confusion, IÆve repeated the entire program here:
  91. //BUDGET5.1c - this version splits the linked list into two
  92. //            by establishing two different pFirst pointers,
  93. //            one to the first Checking account object and
  94. //            the other to the first Savings account object.
  95. //
  96. //            This is the solution to problem 1c.
  97. //
  98.  
  99. #include <iostream.h>
  100. #include <stdlib.h>
  101. #include <ctype.h>
  102. //Account - this abstract class incorporates properties
  103. //          common to both account types Checking and
  104. //          Savings; however, itÆs missing the concept
  105. //          withdrawal( ) which is different between the two
  106. class Account
  107. {
  108.   protected:
  109.    Account(Account &c)
  110.    {
  111.       cout << ôNo creating funds\nö;
  112.    }
  113.  
  114.    //this function adds an object to the list pointed
  115.    //at by the argument provided to the function
  116.    void addToList(Account * &pFirst);            //Note 1
  117.  
  118.   public:
  119.    Account(unsigned accNo, float initialBalance = 0.0F);
  120.  
  121.    //access functions
  122.    int accountNo( )
  123.    {
  124.       return accountNumber;
  125.    }
  126.    float acntBalance( )
  127.    {
  128.       return balance;
  129.    }
  130.  
  131.    static int noAccounts( )
  132.    {
  133.       return count;
  134.    }
  135.  
  136.    //transaction functions
  137.    void deposit(float amount)
  138.    {
  139.       balance += amount;
  140.    }
  141.    virtual void withdrawal(float amount) = 0;
  142.  
  143.    //display function for displaying self on æcoutÆ
  144.    void display( )
  145.    {
  146.       cout << ôAccount ô << accountNumber
  147.            << ô = ô      << balance
  148.            << ô\nö;
  149.    }
  150.  
  151.    //make the following functions pure virtual
  152.    Account *next( )
  153.    {
  154.       return pNext;
  155.    }
  156.  
  157.  
  158.   protected:
  159.    static int count;         //number of accounts
  160.    unsigned   accountNumber;
  161.    float      balance;
  162.  
  163.    //pNext is still in Account but pFirst        //Note 2
  164.    //has now been relegated to the subclasses
  165.    Account   *pNext;
  166. };
  167. int Account::count = 0;
  168.  
  169. Account::Account(unsigned accNo, float initialBalance)
  170. {
  171.    accountNumber = accNo;
  172.    balance = initialBalance;
  173.    count++;
  174. }
  175.  
  176. //addToList - by accepting a reference to pFirst
  177. //            as an argument, addToList( ) can be called
  178. //            from either Checking or Savings
  179. void Account::addToList(Account * &pFirst)
  180. {
  181.    //this comes out of the constructor for Account
  182.    if (pFirst == 0)
  183.    {
  184.       pFirst = this;      //empty list; make it first
  185.    }
  186.    else {                 //list not empty; look for last...
  187.                           //...entry in the list
  188.       for (Account *pA = pFirst; pA->pNext; pA = pA->pNext)
  189.       {
  190.       }
  191.       pA->pNext = this;   //tack us onto end
  192.    }
  193.    pNext = 0;             //weÆre always last
  194. }
  195.  
  196. //Checking - this class contains properties unique to
  197. //           checking accounts.  Not much left is there?
  198. class Checking : public Account
  199. {
  200.   public:
  201.    //here the constructor defined inline
  202.    Checking(unsigned accNo, float initialBalance = 0.0F) :
  203.        Account(accNo, initialBalance)
  204.    {
  205.      addToList(Checking::pFirst);                //Note 3
  206.    }
  207.  
  208.    //overload pure virtual functions
  209.    virtual void withdrawal(float amount);
  210.  
  211.    //return first object in Checking account list
  212.    static Account* first( )                       //Note 4
  213.    {
  214.       return (Account*)Checking::pFirst;
  215.    }
  216.   protected:
  217.    static Account* pFirst;
  218. };
  219. Account *Checking::pFirst = 0;
  220.  
  221. void Checking::withdrawal(float amount)
  222. {
  223.    if (balance < amount)
  224.    {
  225.       cout << ôInsufficient funds: balance ô << balance
  226.            << ô, check ô                     << amount
  227.            << ô\nö;
  228.    }
  229.    else
  230.    {
  231.       balance -= amount;
  232.  
  233.       //if balance falls too low, charge service fee
  234.       if (balance < 500.00F)
  235.       {
  236.          balance -= 0.20F;
  237.       }
  238.    }
  239. }
  240.  
  241.  
  242. //Savings - same story as Checking except that it also
  243. //          has a unique data member
  244. class Savings : public Account
  245. {
  246.   public:
  247.    //here the constructor is defined as a separate
  248.    //function just to show you the difference
  249.    Savings(unsigned accNo, float initialBalance = 0.0F) :
  250.       Account(accNo, initialBalance)
  251.    {
  252.       noWithdrawals = 0;
  253.       addToList(Savings::pFirst);
  254.    }
  255.  
  256.    //transaction functions
  257.    virtual void withdrawal(float amount);
  258.    static Account* first( )
  259.    {
  260.       return (Account*)Savings::pFirst;
  261.    }
  262.  
  263.  
  264.   protected:
  265.    int        noWithdrawals;
  266.    static Account *pFirst;
  267. };
  268. Account* Savings::pFirst = 0;
  269.  
  270. void Savings::withdrawal(float amount)
  271. {
  272.    if (balance < amount)
  273.    {
  274.       cout << ôInsufficient funds: balance ô << balance
  275.            << ô, withdrawal ô                << amount
  276.            << ô\nö;
  277.    }
  278.    else
  279.    {
  280.       if (++noWithdrawals > 1)
  281.       {
  282.          balance -= 5.00F;
  283.       }
  284.       balance -= amount;
  285.    }
  286. }
  287.  
  288. //prototype declarations
  289. unsigned getAccntNo( );
  290. void process(Account &account);
  291. void outOfMemory( );
  292. int main( );
  293.  
  294. //main - accumulate the initial input and output totals
  295. int main( )
  296. {
  297.    /*loop until someone enters an æXÆ or æxÆ*/
  298.    Account *pA;
  299.    char     accountType;     //S or C
  300.  
  301.    unsigned keepLooping = 1;
  302.    while (keepLooping)
  303.    {
  304.       cout << ôEnter S for Savings, ô
  305.               ôC for Checking, X for exit\nö;
  306.       cin  >> accountType;
  307.  
  308.       switch (accountType)
  309.       {
  310.          case æcÆ:
  311.          case æCÆ:
  312.             pA = new Checking(getAccntNo( ));
  313.             if (pA == 0)
  314.             {
  315.                outOfMemory( );
  316.             }
  317.             process(*pA);
  318.             break;
  319.  
  320.          case æsÆ:
  321.          case æSÆ:
  322.             pA = new Savings(getAccntNo( ));
  323.             if (pA == 0)
  324.             {
  325.                outOfMemory( );
  326.             }
  327.             process(*pA);
  328.             break;
  329.          case æxÆ:
  330.          case æXÆ:
  331.             keepLooping = 0;
  332.             break;
  333.  
  334.          default:
  335.             cout << ôI didnÆt get that.\nö;
  336.       }
  337.    }
  338.  
  339.    //now present totals                          //Note 5
  340.    float subTotal = 0.0F;
  341.    float total = 0.0F;
  342.    cout << ôAccount totals:\nö;
  343.  
  344.    //we are now forced to display the lists separately
  345.    for (pA = Checking::first( ); pA; pA = pA->next( ))
  346.    {
  347.       pA->display( );
  348.       subTotal += pA->acntBalance( );
  349.    }
  350.    cout << ôTotal of all checking accounts = ô << subTotal << ô\nö;
  351.    total += subTotal;
  352.  
  353.    //repeat the process for savings
  354.    subTotal = 0.0F;
  355.    for (pA = Savings::first( ); pA; pA = pA->next( ))
  356.    {
  357.       pA->display( );
  358.       subTotal += pA->acntBalance( );
  359.    }
  360.    cout << ôTotal of all savings accounts = ô << subTotal << ô\nö;
  361.    total += subTotal;
  362.  
  363.    cout << ôTotal worth of all accounts   = ô << total << ô\nö;
  364.    return 0;
  365. }
  366.  
  367. //getAccntNo - return the account number entered
  368. unsigned getAccntNo( )
  369. {
  370.    unsigned accntNo;
  371.    cout << ôEnter account number:ö;
  372.    cin  >> accntNo;
  373.    return accntNo;
  374. }
  375.  
  376. //process(Account) - input the data for an account*/
  377. void process(Account &account)
  378. {
  379.    cout << ôEnter positive number for deposit,\nö
  380.            ônegative for withdrawal, 0 to terminateö;
  381.  
  382.    float transaction;
  383.    do
  384.    {
  385.       cout << ô:ö;
  386.       cin  >> transaction;
  387.  
  388.       //deposit
  389.       if (transaction > 0.0F)
  390.       {
  391.          account.deposit(transaction);
  392.       }
  393.  
  394.       //withdrawal
  395.       if (transaction < 0.0F) {
  396.          account.withdrawal(-transaction);
  397.       }
  398.    } while (transaction != 0.0F);
  399. }
  400.  
  401. //outOfMemory - generate out-of-memory message and quit
  402. void outOfMemory( )
  403. {
  404.    cout << ôOut of memory\nö;
  405.    abort( );
  406. }
  407.         The goal is to split the linked list of Account objects into two linked lists, one of 
  408. Checking objects and another of Savings objects. To do so, the static data member 
  409. pFirst has been moved out of the class Account (Note 2) and into the classes 
  410. Checking and Savings (Note 4). Giving each subclass its own pFirst pointer allows 
  411. them to maintain their own separate linked lists. This change means that the member 
  412. function first() must be moved into the subclasses as well. (The pointer pNext does 
  413. not need to be moved because both types of accounts include pointers to the next 
  414. member of their respective list.)
  415.         Because pFirst is no longer a member of Account, the constructor for Account cannot 
  416. refer it. Looked at another way, the constructor for Account cannot add the object to 
  417. one of the lists because it does not know which list to add it to: The object is not yet 
  418. either a Savings account object or a Checking account object.
  419.         The job of adding the object to the linked list must be moved into the constructor for 
  420. Savings and Checking (Note 3). The work of adding an object to the list is the same 
  421. for both classes, so there is not a problem with these classes calling a common 
  422. function and passing the proper pFirst pointer upon which to operate (Note 1). (The 
  423. argument to addToList() is a reference to a pointer so that the function can change the 
  424. value of the pointer passed to it.)
  425.         The remainder of the program is the same as in BUDGET5 until the end of main(), 
  426. where the program displays the lists of accounts. Rather than one loop as before, the 
  427. program must now loop twice: the first time starting with the object returned from 
  428. Checking::first(), that is, the first checking account, and a second time starting with 
  429. the object returned by Savings::first(), that is, the first savings account (Note 5).
  430.         The output from this program appears as follows:
  431.     Enter S for Savings, C for Checking, X for exit
  432.     S
  433.     Enter account number:1234
  434.     Enter positive number for deposit,
  435.     negative for withdrawal, 0 to terminate:100
  436.     :0
  437.     Enter S for Savings, C for Checking, X for exit
  438.     C
  439.     Enter account number:2345
  440.     Enter positive number for deposit,
  441.     negative for withdrawal, 0 to terminate:200
  442.     :0
  443.     Enter S for Savings, C for Checking, X for exit
  444.     S
  445.     Enter account number:3456
  446.     Enter positive number for deposit,
  447.     negative for withdrawal, 0 to terminate:300
  448.     :0
  449.     Enter S for Savings, C for Checking, X for exit
  450.     C
  451.     Enter account number:4567
  452.     Enter positive number for deposit,
  453.     negative for withdrawal, 0 to terminate:400
  454.     :0
  455.     Enter S for Savings, C for Checking, X for exit
  456.     X
  457.     Account totals:
  458.     Account 2345 = 200
  459.     Account 4567 = 400
  460.     Total of all checking accounts = 600
  461.     Account 1234 = 100
  462.     Account 3456 = 300
  463.     Total of all savings accounts = 400
  464.     Total worth of all accounts   = 1000
  465.         A second approach is to leave the objects mixed in the same linked list and divide 
  466. them during the display process. This requires the addition of a run-time function (as 
  467. I explain in Chapter 19), called runtimeType() here. The return from runtimeType() is 
  468. used to discriminate the objects. The class additions are shown in Chapter 19, so only 
  469. the relevant section of main() appears here:
  470.     //now present totals
  471.     float cTotal = 0.0;
  472.     float sTotal = 0.0;
  473.     cout << ôAccount totals:\nö;
  474.     for (pA = Account::first( ); pA; pA = pA->next( ))
  475.     {
  476.        pA->display( );
  477.         //keep checking and saving account totals separate
  478.         switch(pA->runtimeType( ))
  479.       {
  480.           case Account::CHECKING:
  481.              cTotal += pA->acntBalance( );
  482.             break;
  483.          case Account::SAVINGS:
  484.             sTotal += pA->acntBalance( );
  485.             break;
  486.          default:
  487.             cout << ôUnknown account type encountered\nö;
  488.       }
  489.     }
  490.     cout << ôChecking account total = ô << cTotal << ô\nö;
  491.     cout << ôSavings  account total = ô << sTotal << ô\nö;
  492.     cout << ôTotal worth            = ô << (cTotal+sTotal) << ô\nö;
  493.         The switch statement keeps the checking and savings account totals in separate piles.
  494.         Neither approach is completely extensible because both must make explicit reference 
  495. to the particular subclasses of Account. However, the first approach is the more 
  496. flexible of the two.
  497. 2
  498. This is one of those great questions because there is no wrong answer. My answer follows, but yours is 
  499. probably just as right (or more so) even if it differs from mine. See CLASS.PCX on the disk for my 
  500. answer. The concrete classes are PanelTruck, 18Wheeler, Pickup, Sedan, StationWagon, and CV2. The rest 
  501. are abstract classes.
  502. I started by dividing vehicles into gasoline- and diesel-powered varieties. The two types of trucks provided 
  503. were both diesel (I assumed), but they differed in that one has a detachable trailer and the other does not. 
  504. The gasoline vehicles were divided along the lines of water cooled and air cooled. Water-cooled vehicles 
  505. were divided into regular cars and cowboy Cadillacs (pickups). Many station wagons are more similar to 
  506. pickups than sedans, so I threw them into the pickup category.
  507. ItÆs good practice to consciously build these types of class hierarchies. So, the next time youÆre lying back 
  508. in the tub or caught in traffic, imagine adding other types of vehicles in this framework.
  509.  
  510.  
  511.  
  512.