C++基礎——類繼承中方法重載


一、前言

       在上一篇C++基礎博文中討論了C++最基本的代碼重用特性——類繼承,派生類可以在繼承基類元素的同時,添加新的成員和方法。但是沒有考慮一種情況:派生類繼承下來的方法的實現細節並不一定適合派生類的需求,此時派生類需要重載集成方法。

二、重載方法及虛函數

    我們討論《C++ Primer Plus》中的如下場景:銀行記錄客戶信息,包括客戶姓名、當前余額。客戶這一類別當然能夠創建客戶對象、存款、取款以及顯示信息。銀行需要特殊記錄具有透支權限的客戶,因此這一類別的客戶要額外記錄透支上限、透支貸款利率以及當前透支總額。此外,取款和顯示信息兩個操作必須考慮客戶的透支情況。綜上,具有透支權限的客戶是客戶這一基類的派生類,派生類中不但需要添加新的成員,還要重載兩個繼承方法。

  類聲明代碼:

 1 #ifndef BRASS_H_
 2 #define BRASS_H_
 3 
 4 #include <string>
 5 
 6 class Brass
 7 {
 8 private:
 9     std::string fullName;
10     long acctNum;
11     double balance;
12 public:
13     Brass(const std::string& s = "Nullbody",long an = -1,double ba = 0.0);//default constructor
14     void Deposit(double amt);
15     double Balance() const;
16     virtual void Withdraw(double amt);//virtual function
17     virtual void ViewAcct() const;
18     virtual ~Brass() {}//使用虛析構函數確保先調用繼承類析構函數
19 };
20 
21 //brass plus account class
22 class BrassPlus:public Brass
23 {
24 private:
25     double maxLoan;
26     double rate;
27     double owesBank;
28 public:
29     BrassPlus(const std::string& s = "Nullbody",long an = -1,
30                 double bal = 0.0,double ml = 500,double r = 0.11125);
31     BrassPlus(const Brass& ba,double ml = 500,double r = 0.11125);
32     virtual void ViewAcct() const;
33     virtual void Withdraw(double amt);
34     void ResetMax(double m) {maxLoan = m;}//inline function
35     void ResetRate(double r) {rate = r;}
36     void ResetOwes() {owesBank = 0;}
37 };
38 
39 #endif
brass.h

  類方法定義代碼:

 1 #include"brass.h"
 2 #include <iostream>
 3 
 4 using std::cout;
 5 using std::endl;
 6 using std::string;
 7 
 8 //brass methods
 9 Brass::Brass(const string& s,long an,double bal)
10 {
11     fullName = s;
12     acctNum = an;
13     balance = bal;
14 }
15 
16 void Brass::Deposit(double amt)
17 {
18     if(amt < 0)
19         cout << "Negative deposit not allowed;"
20             << "deposit is cancelled.\n";
21     else
22         balance += amt;
23 }
24 
25 void Brass::Withdraw(double amt)
26 {
27     if(amt < 0)
28         cout << "Withdrawal amount must be positive;"
29             << "withdrawal canceled.\n";
30     else if (amt <= balance)
31         balance -= amt;
32     else
33         cout << "Withdrawal amount of $" << amt 
34             << "exceeds your balance.\n"
35             << "Withdrawal canceled.\n";
36 }
37 
38 double Brass::Balance() const
39 {
40     return balance;
41 }
42 
43 void Brass::ViewAcct() const
44 {
45     cout << "Client: " << fullName << endl;
46     cout << "Account Number: " << acctNum << endl;
47     cout << "Balance: $" << balance << endl;
48 }
49 
50 //brassPlus methods
51 BrassPlus::BrassPlus(const string& s,long an,double bal,
52                      double ml,double r):Brass(s,an,bal)
53 {
54     maxLoan = ml;
55     owesBank = 0.0;
56     rate = r;
57 }
58 
59 BrassPlus::BrassPlus(const Brass& ba,double ml,double r):Brass(ba)
60 {
61     maxLoan = ml;
62     owesBank = 0.0;
63     rate = r;
64 }
65 
66 //redefine viewacct()
67 void BrassPlus::ViewAcct() const
68 {
69     Brass::ViewAcct();
70     cout << "Maximum loan: $" << maxLoan << endl;
71     cout << "Owed to bank: $" << owesBank << endl;
72 }
73 
74 void BrassPlus::Withdraw(double amt)
75 {
76     double bal = Balance();
77     if(amt <= bal)
78         Brass::Withdraw(amt);
79     else if(amt <= bal + maxLoan - owesBank)// 已欠 + 此欠 ≤ maxLoan
80     {
81         double advance = amt - bal;
82         owesBank += advance * (1.0+rate);
83         cout << "Bank advance: $" << advance << endl;
84         cout << "Finance charge: $" << advance*rate << endl;
85         Deposit(advance);
86         Brass::Withdraw(amt);// return to zero
87     }
88     else
89         cout << "Credit limit exceeded. Transcation cancelled.\n" ;
90 }
brass.cpp

   上述代碼多了一個新的語法特性:虛函數(virtual function)。當基類聲明中函數前加virtual,表示該函數為虛函數。區別在於當調用者是引用或者指針時,調用的是基類方法,還是派生類重載后的方法。具體區別我們后邊在討論。重中之重在於虛析構函數的意義。如果程序中使用delete刪除占用的動態內存,且用於索引內存地址的指針類型是基類,那么即使該指針指向的是一個派生類對象,此時僅基類析構函數被調用。 我們着重觀察brassPlus類重載的方法WithDraw有什么變化。這類客戶由於具有透支權限,在取款時肯定要考慮欠款情況。若欲取出金額≤存儲金額,則直接調用基類方法WithDraw,把存儲金額減小;若欲取出金額大於存儲金額,就必須進一步分析欠款情況。已欠款+此次欠款≤透支額度時,取款操作才有效。因此:owes+(amt - balance) ≤ maxLoan,進一步變形為:amt ≤ balance+maxLoan-owes。

三、應用程序示例及結果分析

 現在看看應用程序代碼和顯示結果。APP代碼:

 1 #include <iostream>
 2 #include "brass.h"
 3 
 4 int main()
 5 {
 6     using std::cout;
 7     using std::endl;
 8 
 9     Brass Piggy("Porcelot Pigg",381299,4000.00);
10     BrassPlus Hoggy("Horatio Hogg",382288,3000.00);
11 
12     Piggy.ViewAcct();
13     cout << endl;
14     Hoggy.ViewAcct();
15     cout << endl;
16     
17     cout << "Depositing $1000 into the Hogg Account:\n";
18     Hoggy.Deposit(1000.00);
19     cout << "New balance: $" <<Hoggy.Balance() <<endl;
20     cout << endl;
21 
22     cout << "Withdrawing $4200 from the Pigg Account:\n";
23     Piggy.Withdraw(4200.00);
24     cout << "Pigg account balance: $" << Piggy.Balance() << endl;
25     cout << endl;
26     
27     cout << "Withdrawing $4200 from the Hogg Account:\n";
28     Hoggy.Withdraw(4200.00);
29     Hoggy.ViewAcct();
30     cout << endl;
31 
32     Brass dom("Dominic Banker",11224,4183.45);
33     BrassPlus dot("Dorothy Banker",12118,2592.00);
34 
35     Brass& b1_ref = dom;
36     Brass& b2_ref = dot;//use BrassPlus::ViewAcct() function
37 
38     b1_ref.ViewAcct();
39     cout << endl;
40     b2_ref.ViewAcct();
41     cout << endl;
42 
43     return 0;
44 }
usebrass.cpp

  打印結果:

   Pigg和Hogg分別是基類和派生類對象。當兩種均取款額度超出存儲金額時,Hogg由於具有透支權限,才得以成功完成操作。注意之后創建的兩個對象dom和dot,從調用ViewAcct()函數過程中再次體會虛函數的意義。若沒有使用virtual關鍵字,程序根據引用或指針的類型選擇使用基類方法還是派生類同名的重載后方法。若使用該關鍵字,則根據引用或指針所指向對象的類型來選擇。程序中,b1_ref和b2_ref均是Brass類引用,但分別是Brass類對象dom和BrassPlus類對象dot的別名,因此使用virtual關鍵字后的ViewAcct()函數,依次調用基類和派生類方法。收工。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com