4.多态
如果没有使用关键字virtual,程序根据引用类型或指针类型选择方法;如果用了virtual,程序将根据引用或指针指向的对象的类型来选择方法。
view plaincopy to clipboardprint? Brass dom(...); BrassPlus dot(...); Brass & b1 = dom; Brass & b2 = dot; //behavior with non-virtual ViewAcct() b1.ViewAcct(); // use Brass::ViewAcct() b2.ViewAcct(); // use Brass::ViewAcct() //behavior with virtual ViewAcct() b1.ViewAcct(); // use Brass::ViewAcct() b2.ViewAcct(); // use BrassPlus::ViewAcct() Brass dom(...); BrassPlus dot(...); Brass & b1 = dom; Brass & b2 = dot; //behavior with non-virtual ViewAcct() b1.ViewAcct(); // use Brass::ViewAcct() b2.ViewAcct(); // use Brass::ViewAcct() //behavior with virtual ViewAcct() b1.ViewAcct(); // use Brass::ViewAcct() b2.ViewAcct(); // use BrassPlus::ViewAcct()
非构造函数不能使用成员初始化列表句法,但派生类方法可以调用公有的基类方法。在派生类方法中,标准的技术是使用作用域解析操作符来调用基类方法。如
view plaincopy to clipboardprint? void BrassPlus::ViewAcct() const { ... Brass::ViewAcct(); ... } void BrassPlus::ViewAcct() const { ... Brass::ViewAcct(); ... }
如果写成void BrassPlus::ViewAcct() const{ ... ViewAcct(); ...}将创建一个不会终止的递归函数。不过如果派生类没有重新定义某基类的方法,则代码不必对该方法使用作用域解析操作符。
假设要同时管理Brass和BrassPlus账户,如果能使用同一个数组来保存Brass和BrassPlus对象,将很有帮助,但这是不可能的,数组中所有元素的类型必须相同。不过,可以创建指向Brass的指针数组,这样,每个元素的类型都相同,但由于是公有继承模型,因此Brass指针既可以指向Brass对象,也可以指向BrassPlus对象。因此,可以使用一个数组来表示多种类型的对象,这就是多态性。
下面是一个多态的例子:
view plaincopy to clipboardprint? // brass.h -- bank account classes #ifndef BRASS_H_ #define BRASS_H_ // Brass Account Class class Brass { private: enum {MAX = 35}; char fullName[MAX]; long acctNum; double balance; public: Brass(const char *s = "Nullbody", long an = -1, double bal = 0.0); void Deposit(double amt); virtual void Withdraw(double amt); double Balance() const; virtual void ViewAcct() const; virtual ~Brass() {} }; //Brass Plus Account Class class BrassPlus : public Brass { private: double maxLoan; double rate; double owesBank; public: BrassPlus(const char *s = "Nullbody", long an = -1, double bal = 0.0, double ml = 500, double r = 0.10); BrassPlus(const Brass & ba, double ml = 500, double r = 0.1); virtual void ViewAcct()const; virtual void Withdraw(double amt); void ResetMax(double m) { maxLoan = m; } void ResetRate(double r) { rate = r; }; void ResetOwes() { owesBank = 0; } }; #endif // brass.h -- bank account classes #ifndef BRASS_H_ #define BRASS_H_ // Brass Account Class class Brass { private: enum {MAX = 35}; char fullName[MAX]; long acctNum; double balance; public: Brass(const char *s = "Nullbody", long an = -1, double bal = 0.0); void Deposit(double amt); virtual void Withdraw(double amt); double Balance() const; virtual void ViewAcct() const; virtual ~Brass() {} }; //Brass Plus Account Class class BrassPlus : public Brass { private: double maxLoan; double rate; double owesBank; public: BrassPlus(const char *s = "Nullbody", long an = -1, double bal = 0.0, double ml = 500, double r = 0.10); BrassPlus(const Brass & ba, double ml = 500, double r = 0.1); virtual void ViewAcct()const; virtual void Withdraw(double amt); void ResetMax(double m) { maxLoan = m; } void ResetRate(double r) { rate = r; }; void ResetOwes() { owesBank = 0; } }; #endif view plaincopy to clipboardprint? // brass.cpp -- bank account class methods #include <iostream> #include <cstring> #include "brass.h" using std::cout; using std::ios_base; using std::endl; // Brass methods Brass::Brass(const char *s, long an, double bal) { std::strncpy(fullName, s, MAX - 1); fullName[MAX - 1] = '\0'; acctNum = an; balance = bal; } void Brass::Deposit(double amt) { if (amt < 0) cout << "Negative deposit not allowed; " << "deposit is cancelled.\n"; else balance += amt; } void Brass::Withdraw(double amt) { if (amt < 0) cout << "Withdrawal amount must be positive; " << "withdrawal canceled.\n"; else if (amt <= balance) balance -= amt; else cout << "Withdrawal amount of $" << amt << " exceeds your balance.\n" << "Withdrawal canceled.\n"; } double Brass::Balance() const { return balance; } void Brass::ViewAcct() const { // set up ###.## format ios_base::fmtflags initialState = cout.setf(ios_base::fixed, ios_base::floatfield); cout.setf(ios_base::showpoint); cout.precision(2); cout << "Client: " << fullName << endl; cout << "Account Number: " << acctNum << endl; cout << "Balance: $" << balance << endl; cout.setf(initialState); // restore original format } // BrassPlus Methods BrassPlus::BrassPlus(const char *s, long an, double bal, double ml, double r) : Brass(s, an, bal) { maxLoan = ml; owesBank = 0.0; rate = r; } BrassPlus::BrassPlus(const Brass & ba, double ml, double r) : Brass(ba) // uses implicit copy constructor { maxLoan = ml; owesBank = 0.0; rate = r; } // redefine how ViewAcct() works void BrassPlus::ViewAcct() const { // set up ###.## format ios_base::fmtflags initialState = cout.setf(ios_base::fixed, ios_base::floatfield); cout.setf(ios_base::showpoint); cout.precision(2); Brass::ViewAcct(); // display base portion cout << "Maximum loan: $" << maxLoan << endl; cout << "Owed to bank: $" << owesBank << endl; cout << "Loan Rate: " << 100 * rate << "%\n"; cout.setf(initialState); } // redefine how Withdraw() works void BrassPlus::Withdraw(double amt) { // set up ###.## format ios_base::fmtflags initialState = cout.setf(ios_base::fixed, ios_base::floatfield); cout.setf(ios_base::showpoint); cout.precision(2); double bal = Balance(); if (amt <= bal) Brass::Withdraw(amt); else if ( amt <= bal + maxLoan - owesBank) { double advance = amt - bal; owesBank += advance * (1.0 + rate); cout << "Bank advance: $" << advance << endl; cout << "Finance charge: $" << advance * rate << endl; Deposit(advance); Brass::Withdraw(amt); } else cout << "Credit limit exceeded. Transaction cancelled.\n"; cout.setf(initialState); } // brass.cpp -- bank account class methods #include <iostream> #include <cstring> #include "brass.h" using std::cout; using std::ios_base; using std::endl; // Brass methods Brass::Brass(const char *s, long an, double bal) { std::strncpy(fullName, s, MAX - 1); fullName[MAX - 1] = '\0'; acctNum = an; balance = bal; } void Brass::Deposit(double amt) { if (amt < 0) cout << "Negative deposit not allowed; " << "deposit is cancelled.\n"; else balance += amt; } void Brass::Withdraw(double amt) { if (amt < 0) cout << "Withdrawal amount must be positive; " << "withdrawal canceled.\n"; else if (amt <= balance) balance -= amt; else cout << "Withdrawal amount of $" << amt << " exceeds your balance.\n" << "Withdrawal canceled.\n"; } double Brass::Balance() const { return balance; } void Brass::ViewAcct() const { // set up ###.## format ios_base::fmtflags initialState = cout.setf(ios_base::fixed, ios_base::floatfield); cout.setf(ios_base::showpoint); cout.precision(2); cout << "Client: " << fullName << endl; cout << "Account Number: " << acctNum << endl; cout << "Balance: $" << balance << endl; cout.setf(initialState); // restore original format } // BrassPlus Methods BrassPlus::BrassPlus(const char *s, long an, double bal, double ml, double r) : Brass(s, an, bal) { maxLoan = ml; owesBank = 0.0; rate = r; } BrassPlus::BrassPlus(const Brass & ba, double ml, double r) : Brass(ba) // uses implicit copy constructor { maxLoan = ml; owesBank = 0.0; rate = r; } // redefine how ViewAcct() works void BrassPlus::ViewAcct() const { // set up ###.## format ios_base::fmtflags initialState = cout.setf(ios_base::fixed, ios_base::floatfield); cout.setf(ios_base::showpoint); cout.precision(2); Brass::ViewAcct(); // display base portion cout << "Maximum loan: $" << maxLoan << endl; cout << "Owed to bank: $" << owesBank << endl; cout << "Loan Rate: " << 100 * rate << "%\n"; cout.setf(initialState); } // redefine how Withdraw() works void BrassPlus::Withdraw(double amt) { // set up ###.## format ios_base::fmtflags initialState = cout.setf(ios_base::fixed, ios_base::floatfield); cout.setf(ios_base::showpoint); cout.precision(2); double bal = Balance(); if (amt <= bal) Brass::Withdraw(amt); else if ( amt <= bal + maxLoan - owesBank) { double advance = amt - bal; owesBank += advance * (1.0 + rate); cout << "Bank advance: $" << advance << endl; cout << "Finance charge: $" << advance * rate << endl; Deposit(advance); Brass::Withdraw(amt); } else cout << "Credit limit exceeded. Transaction cancelled.\n"; cout.setf(initialState); } view plaincopy to clipboardprint? // usebrass2.cpp -- polymorphic example // compile with brass.cpp #include <iostream> #include "brass.h" const int CLIENTS = 4; const int LEN = 40; int main() { using std::cin; using std::cout; using std::endl; Brass * p_clients[CLIENTS]; int i; for (i = 0; i < CLIENTS; i++) { char temp[LEN]; long tempnum; double tempbal; char kind; cout << "Enter client's name: "; cin.getline(temp, LEN); cout << "Enter client's account number: "; cin >> tempnum; cout << "Enter opening balance: $"; cin >> tempbal; cout << "Enter 1 for Brass Account or " << "2 for BrassPlus Account: "; while (cin >> kind && (kind != '1' && kind != '2')) cout <<"Enter either 1 or 2: "; if (kind == '1') p_clients = new Brass(temp, tempnum, tempbal); else { double tmax, trate; cout << "Enter the overdraft limit: $"; cin >> tmax; cout << "Enter the interest rate " << "as a decimal fraction: "; cin >> trate; p_clients = new BrassPlus(temp, tempnum, tempbal, tmax, trate); } while (cin.get() != '\n') continue; } cout << endl; for (i = 0; i < CLIENTS; i++) { p_clients ->ViewAcct(); cout << endl; } for (i = 0; i < CLIENTS; i++) { delete p_clients ; // free memory } cout << "Done.\n"; return 0; } // usebrass2.cpp -- polymorphic example // compile with brass.cpp #include <iostream> #include "brass.h" const int CLIENTS = 4; const int LEN = 40; int main() { using std::cin; using std::cout; using std::endl; Brass * p_clients[CLIENTS]; int i; for (i = 0; i < CLIENTS; i++) { char temp[LEN]; long tempnum; double tempbal; char kind; cout << "Enter client's name: "; cin.getline(temp, LEN); cout << "Enter client's account number: "; cin >> tempnum; cout << "Enter opening balance: $"; cin >> tempbal; cout << "Enter 1 for Brass Account or " << "2 for BrassPlus Account: "; while (cin >> kind && (kind != '1' && kind != '2')) cout <<"Enter either 1 or 2: "; if (kind == '1') p_clients = new Brass(temp, tempnum, tempbal); else { double tmax, trate; cout << "Enter the overdraft limit: $"; cin >> tmax; cout << "Enter the interest rate " << "as a decimal fraction: "; cin >> trate; p_clients = new BrassPlus(temp, tempnum, tempbal, tmax, trate); } while (cin.get() != '\n') continue; } cout << endl; for (i = 0; i < CLIENTS; i++) { p_clients ->ViewAcct(); cout << endl; } for (i = 0; i < CLIENTS; i++) { delete p_clients ; // free memory } cout << "Done.\n"; return 0; }