• 第13章 类继承


    13.1 一个简单的基类

    从一个类派生出另一个类,原始类成为基类,继承类称为派生类。

    13.2 继承:is-a关系

    • 共有继承   is-a
    • 保护继承
    • 私有继承

    13.3多态公有继承

    虚函数:无virtual 程序根据引用类型或指针类型选择方法,有virtual程序将根据引用或指针指向的对象的类型做选择。

    支票代码如下:

    brass.h

    1. #ifndef BRASS_H_
    2. #define BRASS_H_
    3. #include <string>
    4. using std::string;
    5. class Brass
    6. {
    7. private:
    8. string fullName;
    9. long acctNum;
    10. double balance;
    11. public:
    12. Brass(const string &s = "NullBody",long an = -1,double bal = 0.0);
    13. void Deposit(double amt);
    14. virtual void Withdraw(double amt);
    15. double Balance() const;
    16. virtual void ViewAcct() const;
    17. virtual ~Brass() {};
    18. };
    19. class BrassPlus : public Brass
    20. {
    21. private:
    22. double maxLoan;
    23. double rate;
    24. double owesBank;
    25. public:
    26. BrassPlus(const string &s= "NullBody",long an = -1,double bal = 0.0,double ml =500,double r = 0.11125 );
    27. BrassPlus(const Brass &ba,double ml = 500,double r = 0.11125);
    28. virtual void ViewAcct() const;
    29. virtual void Withdraw(double amt);
    30. void ResetMax(double m) {maxLoan = m;}
    31. void ResetRate(double r) {rate = r;}
    32. void ResetOwes() {owesBank = 0;}
    33. };
    34. #endif

    brass.cpp

    1. #include <iostream>
    2. #include "brass.h"
    3. using namespace std;
    4. typedef ios_base::fmtflags format;
    5. typedef streamsize precis;
    6. format setFormat();
    7. void restore(format f,precis p);
    8. Brass::Brass(const string &s,long an,double bal)
    9. {
    10. fullName = s;
    11. acctNum = an;
    12. balance = bal;
    13. }
    14. void Brass::Deposit(double amt)
    15. {
    16. if(amt < 0)
    17. cout << "Negative deposit, deposit is cancelled.\n";
    18. else
    19. balance += amt;
    20. }
    21. void Brass::Withdraw(double amt)
    22. {
    23. format initState = setFormat();
    24. precis prec = cout.precision(2);
    25. if(amt < 0)
    26. cout << "Withdraw canceled\n";
    27. else if(amt <= balance)
    28. balance -= amt;
    29. else
    30. cout << "Withdraw amount of $" << amt << " exceeds your balance.\n" << "Withdraw canceled\n";
    31. restore(initState,prec);
    32. }
    33. double Brass::Balance() const
    34. {
    35. return balance;
    36. }
    37. void Brass::ViewAcct() const
    38. {
    39. format initialstate = setFormat();
    40. precis prec = cout.precision(2);
    41. cout << "Client: " << fullName << endl;
    42. cout << "Account Number:" << acctNum << endl;
    43. cout << "Balance: $" << balance << endl;
    44. restore(initialstate,prec);
    45. }
    46. BrassPlus::BrassPlus(const string &s,long an,double bal,double ml,double r):Brass(s,an,bal)
    47. {
    48. maxLoan = ml;
    49. owesBank = 0.0;
    50. rate = r;
    51. }
    52. BrassPlus::BrassPlus(const Brass & ba,double ml,double r) : Brass(ba)
    53. {
    54. maxLoan = ml;
    55. owesBank = 0.0;
    56. rate = r;
    57. }
    58. void BrassPlus::ViewAcct() const
    59. {
    60. format initialstate = setFormat();
    61. precis prec = cout.precision(2);
    62. Brass::ViewAcct();
    63. cout << "Maxinum loan:$" << maxLoan <<endl;
    64. cout << "Owed to bank:$" << owesBank << endl;
    65. cout.precision(3);
    66. cout << "Loan Rate: " << 100*rate << "%\n";
    67. restore(initialstate,prec);
    68. }
    69. void BrassPlus::Withdraw(double amt)
    70. {
    71. format initialstate = setFormat();
    72. precis prec = cout.precision(2);
    73. double bal = Balance();
    74. if(amt <= bal)
    75. Brass::Withdraw(amt);
    76. else if(amt <= bal + maxLoan - owesBank)
    77. {
    78. double advance = amt - bal;
    79. owesBank += advance * (1.0 + rate);
    80. cout << "Bank advance:$" << advance << endl;
    81. cout << "Finance charge:$" << advance * rate << endl;
    82. Deposit(advance);
    83. Brass::Withdraw(amt);
    84. }
    85. else
    86. cout << "Transaction cancelled.\n";
    87. restore(initialstate,prec);
    88. }
    89. format setFormat()
    90. {
    91. return cout.setf(ios_base::fixed,ios_base::floatfield);
    92. }
    93. void restore(format f,precis p)
    94. {
    95. cout.setf(f,ios_base::floatfield);
    96. cout.precision(p);
    97. }

    usebrass1.cpp

    1. #include <iostream>
    2. #include "brass.h"
    3. using namespace std;
    4. int main()
    5. {
    6. Brass pig("pigg",381299,4000.00);
    7. BrassPlus Hoggy("Hogg",382288,3000.00);
    8. pig.ViewAcct();
    9. cout << endl;
    10. Hoggy.ViewAcct();
    11. cout << endl;
    12. cout << "Hogg + 1000$\n";
    13. Hoggy.Deposit(1000);
    14. cout << "Hoggy bal:$" << Hoggy.Balance() << endl;
    15. cout <<"withdeaw $4200 from pig\n";
    16. pig.Withdraw(4200);
    17. cout <<"pig bal:$" << pig.Balance() << endl;
    18. cout <<"withdraw $4200 from Hogg\n";
    19. Hoggy.Withdraw(4200);
    20. Hoggy.ViewAcct();
    21. return 0;
    22. }

    虚方法行为  多态性

    usebrass2.cpp

    1. #include <iostream>
    2. #include <string>
    3. #include "brass.h"
    4. const int CLIENTS = 4;
    5. using namespace std;
    6. int main()
    7. {
    8. Brass* p_client[CLIENTS];
    9. string temp;
    10. long tempnum;
    11. double tempbal;
    12. char kind;
    13. for(int i=0;i<CLIENTS;i++)
    14. {
    15. cout << "enter client's name:";
    16. getline(cin,temp);
    17. cout << "enter client's accout number:";
    18. cin >> tempnum;
    19. cout << "enter opening balance:$";
    20. cin >> tempbal;
    21. cout << "enter 1 for brass accout or 2 for brassplus accout:";
    22. while(cin >> kind && (kind != '1' && kind != '2'))
    23. cout << "enter 1 or 2:";
    24. if(kind == '1')
    25. p_client[i] = new Brass(temp,tempnum,tempbal);
    26. else
    27. {
    28. double tmax,trate;
    29. cout << "enter overdraft limit:&";
    30. cin >> tmax;
    31. cout << "enter the rate:";
    32. cin >> trate;
    33. p_client[i] = new BrassPlus(temp,tempnum,tempbal,tmax,trate);
    34. }
    35. while(cin.get() != '\n')
    36. continue;
    37. }
    38. cout << endl;
    39. for(int i =0;i<CLIENTS;i++)
    40. {
    41. p_client[i]->ViewAcct();
    42. cout << endl;
    43. delete p_client[i];
    44. }
    45. cout << "Done!\n";
    46. return 0;
    47. }

    运行结果:

    1. enter client's name:harry
    2. enter client's accout number:112233
    3. enter opening balance:$1500
    4. enter 1 for brass accout or 2 for brassplus accout:1
    5. enter client's name:dinah
    6. enter client's accout number:121213
    7. enter opening balance:$1800
    8. enter 1 for brass accout or 2 for brassplus accout:2
    9. enter overdraft limit:&350
    10. enter the rate:0.12
    11. enter client's name:brenda
    12. enter client's accout number:212118
    13. enter opening balance:$5200
    14. enter 1 for brass accout or 2 for brassplus accout:2
    15. enter overdraft limit:&800
    16. enter the rate:0.1
    17. enter client's name:tim
    18. enter client's accout number:233255
    19. enter opening balance:$688
    20. enter 1 for brass accout or 2 for brassplus accout:1
    21. Client: harry
    22. Account Number:112233
    23. Balance: $1500.00
    24. Client: dinah
    25. Account Number:121213
    26. Balance: $1800.00
    27. Maxinum loan:$350.00
    28. Owed to bank:$0.00
    29. Loan Rate: 12.000%
    30. Client: brenda
    31. Account Number:212118
    32. Balance: $5200.00
    33. Maxinum loan:$800.00
    34. Owed to bank:$0.00
    35. Loan Rate: 10.000%
    36. Client: tim
    37. Account Number:233255
    38. Balance: $688.00
    39. Done!

    注意虚函数viewAcct()的调用。

    基类的虚析构函数是很有必要的。

    13.4 静态联编和动态联编

    程序中有重载函数时,编译器根据参数完成函数对应,会再编译过程中完成联编,称为静态联编。

    增加虚函数后,编译器需要在程序运行时对应函数,称为动态联编。

    将派生类引用或指针 转换为 基类引用或者指针 称为向上强制转换,这使公有继承不需要进行显示类型转换。is-a规则

    1. void fr(Brass &rb); //uses rb.ViewAcct()
    2. void fp(Brass *rp); //uses rp->ViewAcct()
    3. void fv(Brass &b); //uses b.ViewAcct()
    4. int main()
    5. {
    6. Brass b("bill",123432,10000);
    7. BrassPlus bp("betty",232323,12345);
    8. fr(b); //uses Brass::ViewAcct()
    9. fr(bp); //uses BrassPlus::ViewAcct()
    10. fp(b); //uses Brass::ViewAcct()
    11. fp(bp); //uses BrassPlus::ViewAcct()
    12. fv(b); //uses Brass::ViewAcct()
    13. fv(bp); //uses Brass::ViewAcct()
    14. }

    虚函数在内存和执行速度方面有一定成本:

    • 每个对象都将增大,增大量为储存地址的空间
    • 对于没个类,编译器都创建一个虚函数地址表
    • 对于每个函数调用,都需执行一项额外操作:表中查找地址

    虚函数还需注意事项

    • 构造函数不能为虚函数
    • 析构函数应该时虚函数,除非类不用做基类
    • 友元不能为虚函数
    • 派生类没有定义函数,会使用基类函数
    • 重新定义将被隐藏方法

    13.5 访问控制:protected

    派生类可以直接访问基类保护成员。

    最好对数据成员采用私有访问控制,不要使用保护控制。

    13.6抽象基类

    abstract base class ABC

    在函数原型中使用=0指出类时一个抽象基类,在类中可以不定义该函数

    代码示例

    acctabc.h

    1. #ifndef ACCTABC_H_
    2. #define ACCTABC_H_
    3. #include <iostream>
    4. #include <string>
    5. class AcctABC
    6. {
    7. private:
    8. std::string fullName;
    9. long acctNum;
    10. double balance;
    11. protected:
    12. struct Formatting
    13. {
    14. std::ios_base::fmtflags flag;
    15. std::streamsize pr;
    16. };
    17. const std::string &FullName() const {return fullName;}
    18. long AcctNum() const {return acctNum;}
    19. Formatting SetFormat() const;
    20. void Restore(Formatting &f) const;
    21. public:
    22. AcctABC(const std::string &s = "Nullbody",long an = -1,double bal = 0.0);
    23. void Deposit(double amt);
    24. virtual void Withdraw(double amt) = 0;
    25. double Balance() const {return balance;}
    26. virtual void ViewAcct() const =0;
    27. virtual ~AcctABC() {};
    28. };
    29. class Brass:public AcctABC
    30. {
    31. public:
    32. Brass(const std::string &s = "Nullbody",long an = -1,double bal = 0.0):AcctABC(s,an,bal) {}
    33. virtual void Withdraw(double amt);
    34. virtual void ViewAcct() const;
    35. virtual ~Brass() {};
    36. };
    37. class BrassPlus:public AcctABC
    38. {
    39. private:
    40. double maxLoan;
    41. double rate;
    42. double owesBank;
    43. public:
    44. BrassPlus(const std::string &s = "Nullbody",long an=-1,double bal = 0.0,double ml = 500,double r=1.10);
    45. BrassPlus(const Brass &ba,double ml = 500,double r = 0.1);
    46. virtual void ViewAcct() const;
    47. virtual void Withdraw(double amt);
    48. void ResetMax(double m) {maxLoan = m;}
    49. void ResetRate(double r) {rate = r;}
    50. void ResetOwes() {owesBank = 0;}
    51. };
    52. #endif

    acctabc.cpp

    1. #include <iostream>
    2. #include "acctabc.h"
    3. using namespace std;
    4. AcctABC::AcctABC(const string &s,long an,double bal)
    5. {
    6. fullName = s;
    7. acctNum = an;
    8. balance = bal;
    9. }
    10. void AcctABC::Deposit(double amt)
    11. {
    12. if(amt < 0)
    13. cout << "Negative deposit, deposit is cancelled.\n";
    14. else
    15. balance += amt;
    16. }
    17. void AcctABC::Withdraw(double amt)
    18. {
    19. balance -= amt;
    20. }
    21. AcctABC::Formatting AcctABC::SetFormat() const
    22. {
    23. Formatting f;
    24. f.flag = cout.setf(ios_base::fixed,ios_base::floatfield);
    25. f.pr = cout.precision(2);
    26. return f;
    27. }
    28. void AcctABC::Restore(Formatting &f) const
    29. {
    30. cout.setf(f.flag,ios_base::floatfield);
    31. cout.precision(f.pr);
    32. }
    33. void Brass::Withdraw(double amt)
    34. {
    35. if(amt < 0)
    36. cout << "Withdraw canceled\n";
    37. else if(amt <= Balance())
    38. AcctABC::Withdraw(amt);
    39. else
    40. cout << "Withdraw amount of $" << amt << " exceeds your balance.\n" << "Withdraw canceled\n";
    41. }
    42. void Brass::ViewAcct() const
    43. {
    44. Formatting f = SetFormat();
    45. cout << "Client: " << FullName() << endl;
    46. cout << "Account Number:" << AcctNum() << endl;
    47. cout << "Balance: $" << Balance() << endl;
    48. Restore(f);
    49. }
    50. BrassPlus::BrassPlus(const string &s,long an,double bal,double ml,double r):AcctABC(s,an,bal)
    51. {
    52. maxLoan = ml;
    53. owesBank = 0.0;
    54. rate = r;
    55. }
    56. BrassPlus::BrassPlus(const Brass & ba,double ml,double r) : AcctABC(ba)
    57. {
    58. maxLoan = ml;
    59. owesBank = 0.0;
    60. rate = r;
    61. }
    62. void BrassPlus::ViewAcct() const
    63. {
    64. Formatting f = SetFormat();
    65. cout << "Client: " << FullName() << endl;
    66. cout << "Account Number:" << AcctNum() << endl;
    67. cout << "Balance: $" << Balance() << endl;
    68. cout << "Maxinum loan:$" << maxLoan <<endl;
    69. cout << "Owed to bank:$" << owesBank << endl;
    70. cout.precision(3);
    71. cout << "Loan Rate: " << 100*rate << "%\n";
    72. Restore(f);
    73. }
    74. void BrassPlus::Withdraw(double amt)
    75. {
    76. Formatting f = SetFormat();
    77. double bal = Balance();
    78. if(amt <= bal)
    79. AcctABC::Withdraw(amt);
    80. else if(amt <= bal + maxLoan - owesBank)
    81. {
    82. double advance = amt - bal;
    83. owesBank += advance * (1.0 + rate);
    84. cout << "Bank advance:$" << advance << endl;
    85. cout << "Finance charge:$" << advance * rate << endl;
    86. Deposit(advance);
    87. AcctABC::Withdraw(amt);
    88. }
    89. else
    90. cout << "Transaction cancelled.\n";
    91. Restore(f);
    92. }

    usebrass3.cpp

    1. #include <iostream>
    2. #include <string>
    3. #include "acctabc.h"
    4. const int CLIENTS = 4;
    5. using namespace std;
    6. int main()
    7. {
    8. AcctABC* p_client[CLIENTS];
    9. string temp;
    10. long tempnum;
    11. double tempbal;
    12. char kind;
    13. for(int i=0;i<CLIENTS;i++)
    14. {
    15. cout << "enter client's name:";
    16. getline(cin,temp);
    17. cout << "enter client's accout number:";
    18. cin >> tempnum;
    19. cout << "enter opening balance:$";
    20. cin >> tempbal;
    21. cout << "enter 1 for brass accout or 2 for brassplus accout:";
    22. while(cin >> kind && (kind != '1' && kind != '2'))
    23. cout << "enter 1 or 2:";
    24. if(kind == '1')
    25. p_client[i] = new Brass(temp,tempnum,tempbal);
    26. else
    27. {
    28. double tmax,trate;
    29. cout << "enter overdraft limit:&";
    30. cin >> tmax;
    31. cout << "enter the rate:";
    32. cin >> trate;
    33. p_client[i] = new BrassPlus(temp,tempnum,tempbal,tmax,trate);
    34. }
    35. while(cin.get() != '\n')
    36. continue;
    37. }
    38. cout << endl;
    39. for(int i =0;i<CLIENTS;i++)
    40. {
    41. p_client[i]->ViewAcct();
    42. cout << endl;
    43. delete p_client[i];
    44. }
    45. cout << "Done!\n";
    46. return 0;
    47. }

    3.7继承和动态内存分配

    未完待续

  • 相关阅读:
    EasyExcel完成excel文件的导入导出
    怎样从零开始训练一个AI车手?
    Servlet面试题
    Python 编程语言的介绍
    图像分割 - 线检测
    在微信小程序中如何引入iconfont
    【Linux私房菜】第三期——实用指令
    CSP测试中完善程序题目的解题经验与运用
    【无标题】
    Git安装
  • 原文地址:https://blog.csdn.net/m0_59666413/article/details/125472723