Chapter 13 类继承
1)一个简单的基类¶
【1】基类原型:
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
C++ 1
2
3
4
5
6
7
8
9
10
//tabtenn0.cpp -- simple base-class methods
#include "tabtenn0.h"
#include <iostream>
TableTennisPlayer::TableTennisPlayer (const string & fn,
const string & ln, bool ht) : firstname(fn),
lastname(ln), hasTable(ht) {}
void TableTennisPlayer::Name() const
{
std::cout << lastname << ", " << firstname;
}
¶
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 |
|
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
C++ | |
---|---|
1 2 3 4 5 |
|
C++ | |
---|---|
1 2 3 4 5 6 7 |
|
【2】派生一个类:
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//tabtenn1.cpp -- simple base-class methods
#include "tabtenn1.h"
#include <iostream>
TableTennisPlayer::TableTennisPlayer (const string & fn,
const string & ln, bool ht) : firstname(fn),
lastname(ln), hasTable(ht) {}
void TableTennisPlayer::Name() const
{
std::cout << lastname << ", " << firstname;
}
// RatedPlayer methods
RatedPlayer::RatedPlayer(unsigned int r, const string & fn,
const string & ln, bool ht) : TableTennisPlayer(fn, ln, ht)
{
rating = r;
}
RatedPlayer::RatedPlayer(unsigned int r, const TableTennisPlayer & tp)
: TableTennisPlayer(tp), rating(r)
{
}
¶
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// usett1.cpp -- using base class and derived class
#include <iostream>
#include "tabtenn1.h"
int main ( void )
{
using std::cout;
using std::endl;
TableTennisPlayer player1("Tara", "Boomdea", false);
RatedPlayer rplayer1(1140, "Mallory", "Duck", true);
rplayer1.Name(); // derived object uses base method
if (rplayer1.HasTable())
cout << ": has a table.\n";
else
cout << ": hasn't a table.\n";
player1.Name(); // base object uses base method
if (player1.HasTable())
cout << ": has a table";
else
cout << ": hasn't a table.\n";
cout << "Name: ";
rplayer1.Name();
cout << "; Rating: " << rplayer1.Rating() << endl;
// initialize RatedPlayer using TableTennisPlayer object
RatedPlayer rplayer2(1212, player1);
cout << "Name: ";
rplayer2.Name();
cout << "; Rating: " << rplayer2.Rating() << endl;
// std::cin.get();
return 0;
}
¶
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
|
(1)派生类写法:
C++ | |
---|---|
1 2 3 4 5 6 7 |
|
(2)派生类需要在继承特性中添加说明:
[1] 派生类需要自己的构造函数
[2] 派生类可以根据需要添加额外的 数据成员 and 成员函数
(3)派生类构造函数的访问权限:
[1] 派生类不能直接访问基类的私有成员,必须通过基类的方法进行访问
[2] 派生类构造函数必须使用基类构造函数
[3] 创建派生类对象时,程序必须首先创建基类对象(基类对象应当在程序进入派生类构造函数之前就被创建)
C++ 1 2 3 4 5 6 7 8 9 10 11RatedPlayer::RatedPlayer (unsigned int r, const string& fn, const string& ln, bool ht) : TableTennisPlayer(fn,ln,ht) { rating = r; } :TableTennisPlayer(fn,ln,ht) 是成员初始化列表【基类构造函数】 该构造函数的执行顺序: (1)RatedPlayer构造函数将把实参 "hbx","dusk","hhh" 赋给形参 fn,ln,ht (2)将这些参数作为实参传递给TableTennisPlayer构造函数 (3)将创建一个TableTennisPlayer对象,并将数据 "hbx" , "dusk" , "hhh" 存储在该对象中 (4)程序进入构造函数体,完成RealPlayer对象的创建,并将参数r的值赋给rating成员
[4] 如果忽略成员初始化列表,程序将使用基类的默认构造函数
C++ 1 2 3 4 5 6 7 8 9 10 11忽略初始化列表: RatedPlayer::RatedPlayer(unsigned int r, const string& fn, const string& ln, bool ht) { rating = r; } ------------------------------------------------------------------------------------------- 等价于: RatedPlayer::RatedPlayer(unsigned int r, const string& fn, const string& ln, bool ht) : TableTennisPlayer() { rating = r; }
[5] 初始化列表的参数改为类成员时
C++ 1 2 3 4 5 6 7 8 9RatedPlayer::RatedPlayer(unsigned int r, const TableTennisPlayer& tp) : TableTennisPlayer(tp) { rating = r; } 由于tp类型为TableTennisPlayer&,因此将调用基类的复制构造函数 基类没有定义复制构造函数,因此编译器将自动生成一个 这种情况下 隐式复制构造函数 是合适的 因为这个类没有使用 动态内存分配!
[6] 为简化也可以对派生类成员使用 成员初始化列表 语法 1)对于继承的基类,初始化列表用的是类名: TableTennisPlayer(tp) 2)对于派生类待初始化的成员,初始化列表用的是成员名:rating(r)
C++ 1
RatedPlayer::RatedPlayer(unsigned int r, const TableTennisPlayer& tp) : TableTennisPlayer(tp),rating(r) {}
【3】继承的基本总结: 1)派生类构造函数的要点:
[1] 首先创建基类对象 [2] 派生类构造函数通过 成员初始化列表 将基类信息传递给基类构造函数 [3] 派生类构造函数应初始化派生类新增的数据成员
2)顺序:
[1] 释放对象的顺序与创建对象的顺序相反!即:先执行派生类的析构函数,再自动调用基类的析构函数 [2] 创建派生类对象时,程序首先调用基类构造函数,然后再调用派生类构造函数(基类构造函数负责初始化继承的数据成员;派生类构造函数主要用于初始化新增的数据成员)
3)派生类和基类的特殊关系:
[1] 派生类对象可以使用基类的方法,条件是:方法不是私有的 [2] 基类指针可以在不进行显式类型转换的情况下指向派生类成员。反之不然! [3] 基类引用可以在不进行显式类型转换的情况下引用派生类成员。反之不然! ps:基类指针/引用 只能用于调用 基类方法 ,不能调用派生类方法
2)多态公有继承¶
(1)问题引入:
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
|
分析: 【1】两个类都声明了 ViewAcct() 和 Withdraw() 方法,但是BrassPlus对象与Brass对象的方法行为是不同的!
【2】对于在两个类中行为相同的方法,如:Deposit() 和 Balance() ,则只在基类中声明!
【3】使用 关键字virtual 的原因:
[1] 如果没有virtual,程序将根据 引用类型 or 指针类型 选择方法 [2] 如果使用virtual,程序将根据 引用对象 or 指针指向的对象 的类型 来选择方法
C++ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21(1) 如果 ViewAcct() 不是虚的,则程序的行为如下: Brass dom("Dominic Banker",11224,4183.45); BrassPlus dot("Dorthy Banker",12118,2592.00); Brass& b1 = dom; Brass& b2 = dot; b1.ViewAcct(); // use Brass::ViewAcct() b2.ViewAcct(); // use Brass::ViewAcct() //引用变量的类型为Brass,所以选择了Brass::ViewAcct() --------------------------------------------------------------------------- (2) 如果 ViewAcct() 是虚的,则程序的行为如下: Brass dom("Dominic Banker",11224,4183.45); BrassPlus dot("Dorthy Banker",12118,2592.00); Brass& b1 = dom; Brass& b2 = dot; b1.ViewAcct(); // use Brass::ViewAcct() b2.ViewAcct(); // use BrassPlus::ViewAcct() //两个引用的类型都是Brass,但是b2引用的是一个BrassPlus对象,所以它是BrassPlus::ViewAcct()
【4】经常在基类中将 派生类会重新定义的 方法声明为虚方法
【5】为基类声明一个虚析构函数是惯例,原因见后面
(2)主程序分析:
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
|
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
常规使用:
// usebrass1.cpp -- testing bank account classes
// compile with brass.cpp
#include <iostream>
#include "brass.h"
int main()
{
using std::cout;
using std::endl;
Brass Piggy("Porcelot Pigg", 381299, 4000.00);
BrassPlus Hoggy("Horatio Hogg", 382288, 3000.00);
Piggy.ViewAcct();
cout << endl;
Hoggy.ViewAcct();
cout << endl;
cout << "Depositing $1000 into the Hogg Account:\n";
Hoggy.Deposit(1000.00);
cout << "New balance: $" << Hoggy.Balance() << endl;
cout << "Withdrawing $4200 from the Pigg Account:\n";
Piggy.Withdraw(4200.00);
cout << "Pigg account balance: $" << Piggy.Balance() << endl;
cout << "Withdrawing $4200 from the Hogg Account:\n";
Hoggy.Withdraw(4200.00);
Hoggy.ViewAcct();
// std::cin.get();
return 0;
}
¶
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
演示虚方法:
// usebrass2.cpp -- polymorphic example
// compile with brass.cpp
#include <iostream>
#include <string>
#include "brass.h"
const int CLIENTS = 4;
int main()
{
using std::cin;
using std::cout;
using std::endl;
Brass * p_clients[CLIENTS];
std::string temp;
long tempnum;
double tempbal;
char kind;
for (int i = 0; i < CLIENTS; i++)
{
cout << "Enter client's name: ";
getline(cin,temp);
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[i] = 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[i] = new BrassPlus(temp, tempnum, tempbal,
tmax, trate);
}
while (cin.get() != '\n')
continue;
}
cout << endl;
for (int i = 0; i < CLIENTS; i++)
{
p_clients[i]->ViewAcct();
cout << endl;
}
for (int i = 0; i < CLIENTS; i++)
{
delete p_clients[i]; // free memory
}
cout << "Done.\n";
/* code to keep window open
if (!cin)
cin.clear();
while (cin.get() != '\n')
continue;
*/
return 0;
}
¶
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
|
分析: 1)关键字virtual只用于类声明的方法原型中,而没有用于方法定义中
C++ | |
---|---|
1 2 3 4 5 |
|
2)派生类不能直接访问基类的私有数据,必须使用基类的公有方法,访问的方式取决于方法
[1] 派生类构造函数在初始化 基类私有数据 时,采用的是==成员初始化列表==的语法
C++ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20// BrassPlus Methods 形式(1) BrassPlus::BrassPlus(const string & s, long an, double bal, double ml, double r) : Brass(s, an, bal) { maxLoan = ml; owesBank = 0.0; rate = r; } ----------------------------------------------------------------------- 形式(2) BrassPlus::BrassPlus(const Brass & ba, double ml, double r) : Brass(ba) // uses implicit copy constructor { maxLoan = ml; owesBank = 0.0; rate = r; } 在这里:const Brass& ba 就是对上文 const string & s + long an + double bal的类对象概括
[2] 非构造函数不能使用初始化列表,但是派生类方法可以调用公有的基类方法
C++ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49// redefine how ViewAcct() works void BrassPlus::ViewAcct() const { // set up ###.## format format initialState = setFormat(); precis prec = cout.precision(2); Brass::ViewAcct(); // display base portion // 演示 基类成员信息 cout << "Maximum loan: $" << maxLoan << endl; // 演示 派生类新增成员信息 cout << "Owed to bank: $" << owesBank << endl; // 演示 派生类新增成员信息 cout.precision(3); // ###.### format cout << "Loan Rate: " << 100 * rate << "%\n"; // 演示 派生类新增成员信息 restore(initialState, prec); } // redefine how Withdraw() works void BrassPlus::Withdraw(double amt) { // set up ###.## format format initialState = setFormat(); precis prec = 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"; restore(initialState, prec); } -------------------------------------------------------------------------------------------- 解释:关于作用域解析运算符::的问题 (1) Brass::ViewAcct(); // display base portion 说明: BrassPlus::ViewAcct() 显示新增的BrassPlus数据成员,并调用基类方法 Brass::ViewAcct() 来显示基类数据成员 这里必须加上 :: ,因为 ViewAcct() 函数存在二义性! (2) double bal = Balance(); // 演示 基类成员信息 说明1: 使用基类的 Balance() 函数确定余额,因为派生类没有重新定义该方法,代码不必对 balance() 使用 :: 【不存在二义性!】 说明2: 这里能够自动识别出来是 Brass::Balance() 还因为:派生类能调用基类的公有方法!
[3] 多态性演示: ![[Pasted image 20230803150341.png]]
多态性的体现在于:
C++ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75演示虚方法: // usebrass2.cpp -- polymorphic example // compile with brass.cpp #include <iostream> #include <string> #include "brass.h" const int CLIENTS = 4; int main() { using std::cin; using std::cout; using std::endl; Brass * p_clients[CLIENTS];----------------------------------- // 建立 指向Brass类型的指针 数组 std::string temp; long tempnum; double tempbal; char kind; for (int i = 0; i < CLIENTS; i++) { cout << "Enter client's name: "; getline(cin,temp); 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[i] = new Brass(temp, tempnum, tempbal);------// 新建 Brass对象 else { double tmax, trate; cout << "Enter the overdraft limit: $"; cin >> tmax; cout << "Enter the interest rate " << "as a decimal fraction: "; cin >> trate; p_clients[i] = new BrassPlus(temp, tempnum, tempbal,---// 新建 BrassPlus对象 tmax, trate); } while (cin.get() != '\n') continue; } cout << endl; for (int i = 0; i < CLIENTS; i++) { p_clients[i]->ViewAcct();---------------------------------// 输出 数组对象(Brass + BrassPlus) cout << endl; } for (int i = 0; i < CLIENTS; i++) { delete p_clients[i]; free memory----------------------// 删除 数组对象(Brass + BrassPlus) } cout << "Done.\n"; /* code to keep window open if (!cin) cin.clear(); while (cin.get() != '\n') continue; */ return 0; }
如果数组成员指向的是Brass对象,则调动 Brass : : ViewAcct( ) ;如果指向的是BrassPlus对象,则调用 BrassPlus : : ViewAcct( ) ps: 如果 BrassPlus : : Acct( )没有被声明为虚的,则在任何情况下都是按照指针类型处理,即调用的是 Brass : : ViewAcct( )
C++ 1 2 3 4 5for (int i = 0; i < CLIENTS; i++) { p_clients[i]->ViewAcct();---------------------------------// 输出 数组对象(Brass + BrassPlus) cout << endl; }
[4] 为什么需要设置虚析构函数: 如果指针指向的是BrassPlus对象,将调用BrassPlus的析构函数,然后自动调用基类的析构函数 【使用虚析构函数可以确保正确的析构函数序列被调用,必须设置!】
3)静态联编和动态联编¶
(1)引入:¶
函数名联编:将源代码中的函数调用解释为执行特定的函数代码块 静态联编:在编译过程中进行的联编(static binding) 动态联编:使用哪一个函数是不能在编译时确定的,因为编译器不知道用户将选择哪种类型的对象。编译器必须生成能够在程序运行时选择正确的虚函数的代码(dynamic binding)
(2)指针和引用类型的兼容性:¶
【1】通常 c++ 不允许将一种类型的地址赋给另一种类型的指针,也不允许一种类型的引用指向另一种类型
C++ | |
---|---|
1 2 3 |
|
【2】指向基类的引用or指针可以引用派生类对象,而不必进行显示类型转换——向上强制转换(upcasting)
C++ | |
---|---|
1 2 3 4 5 |
|
【3】将 基类指针or引用 转换为 派生类指针or引用 ——向下强制转换(downcasting),必须显式类型转换!
【4】对于使用基类引用or指针作为参数的函数调用,将进行upcasting ![[Pasted image 20230804102524.png]] 说明:按值传递只将BrassPlus对象的Brass部分传递给函数 fv( ) 。但随引用和指针发生的upcasting导致fr( )与fp( ) 分别为Brass对象和BrassPlus对象使用Brass : : ViewAcct() 和 BrassPlus : : ViewAcct()
(3)虚函数工作原理:¶
![[Pasted image 20230804103849.png]] ![[Pasted image 20230804103905.png]] ![[Pasted image 20230804104020.png]]
(4)虚函数的注意事项:¶
【基础知识】 ![[Pasted image 20230804104253.png]]
【深入了解】 (1)构造函数
构造函数不能是虚函数 创建派生类对象时,将调用派生类的构造函数,而不是基类的构造函数,然后,派生类的构造函数将使用基类的一个构造函数,这种顺序不是继承机制
(2)析构函数
析构函数必须是虚函数(通常应给基类加一个虚析构函数,即使它并不需要析构) ![[Pasted image 20230804104802.png]]
(3)友元
友元不能是虚函数,因为友元不是类成员,只有成员才能是虚函数
(4)重新定义将隐藏方法 ![[Pasted image 20230804105132.png]] ![[Pasted image 20230804105320.png]] ![[Pasted image 20230804105355.png]] ![[Pasted image 20230804105408.png]]
4)访问控制:protected¶
(1)关键字protected与private相似,在类外只有用公有类成员来访问protected部分的类成员 (2)private与protected之间的区别:派生类成员可以直接访问基类的protected成员;但不能直接访问基类的私有成员 ![[Pasted image 20230804105727.png]] ![[Pasted image 20230804105815.png]]
5)抽象基类(abstract base class,ABC)¶
(1)引入:¶
![[Pasted image 20230804111915.png]] ![[Pasted image 20230804112147.png]] ![[Pasted image 20230804112158.png]]
![[Pasted image 20230804112243.png]]
(2)示例应用:¶
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// acctabc.h -- bank account classes
#ifndef ACCTABC_H_
#define ACCTABC_H_
#include <iostream>
#include <string>
// Abstract Base Class
class AcctABC
{
private:
std::string fullName;
long acctNum;
double balance;
protected:
struct Formatting
{
std::ios_base::fmtflags flag;
std::streamsize pr;
};
const std::string & FullName() const {return fullName;}
long AcctNum() const {return acctNum;}
Formatting SetFormat() const;
void Restore(Formatting & f) const;
public:
AcctABC(const std::string & s = "Nullbody", long an = -1,
double bal = 0.0);
void Deposit(double amt) ;
virtual void Withdraw(double amt) = 0; -------------------------------------- pure virtual function
double Balance() const {return balance;};
virtual void ViewAcct() const = 0; -------------------------------------- pure virtual function
virtual ~AcctABC() {} -------------------------------------- 别忘了虚析构函数
};
// Brass Account Class
class Brass :public AcctABC
{
public:
Brass(const std::string & s = "Nullbody", long an = -1,
double bal = 0.0) : AcctABC(s, an, bal) { } ------------------------普通的就直接继承ABC
virtual void Withdraw(double amt); ------------------------继承ABC的纯虚函数 => 虚函数
virtual void ViewAcct() const; ------------------------继承ABC的纯虚函数 => 虚函数
virtual ~Brass() {}
};
//Brass Plus Account Class
class BrassPlus : public AcctABC
{
private:
double maxLoan;
double rate;
double owesBank; ---------------------------------------- 新增 3 个数据成员
public:
BrassPlus(const std::string & s = "Nullbody", long an = -1,
double bal = 0.0, double ml = 500,
double r = 0.10); ------------------------普通的就直接继承ABC
BrassPlus(const Brass & ba, double ml = 500, double r = 0.1);
virtual void ViewAcct()const; ------------------------继承ABC的纯虚函数 => 虚函数
virtual void Withdraw(double amt); ------------------------继承ABC的纯虚函数 => 虚函数
void ResetMax(double m) { maxLoan = m; }
void ResetRate(double r) { rate = r; };
void ResetOwes() { owesBank = 0; } ------------------------新增 3 个派生类成员函数
};
#endif
¶
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
|
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// acctabc.cpp -- bank account class methods
#include <iostream>
#include "acctabc.h"
using std::cout;
using std::ios_base;
using std::endl;
using std::string;
// Abstract Base Class
AcctABC::AcctABC(const string & s, long an, double bal)--------------------------ABC抽象基类的初始化函数
{
fullName = s;
acctNum = an;
balance = bal;
}
void AcctABC::Deposit(double amt)------------------------------------------------ABC抽象基类的某成员函数
{
if (amt < 0)
cout << "Negative deposit not allowed; "
<< "deposit is cancelled.\n";
else
balance += amt;
}
void AcctABC::Withdraw(double amt)
{
balance -= amt;
}
// protected methods for formatting
AcctABC::Formatting AcctABC::SetFormat() const
{
// set up ###.## format
Formatting f;
f.flag =
cout.setf(ios_base::fixed, ios_base::floatfield);
f.pr = cout.precision(2);
return f;
}
void AcctABC::Restore(Formatting & f) const
{
cout.setf(f.flag, ios_base::floatfield);
cout.precision(f.pr);
}
// Brass methods
void Brass::Withdraw(double amt)---------------------------------------------派生类1的某成员函数(虚函数)
{
if (amt < 0)
cout << "Withdrawal amount must be positive; "
<< "withdrawal canceled.\n";
else if (amt <= Balance())-----------------------------------------------继承的是ABC基类,因此可以直接调用基类方法Balance()
AcctABC::Withdraw(amt);----------------------------------------------继承的是ABC基类,因此可以调用基类方法Withdraw()
else
cout << "Withdrawal amount of $" << amt
<< " exceeds your balance.\n"
<< "Withdrawal canceled.\n";
}
void Brass::ViewAcct() const ---------------------------------------------派生类1的某成员函数(虚函数)
{
Formatting f = SetFormat();
cout << "Brass Client: " << FullName() << endl; ---------------------继承的是ABC基类,因此可以直接调用基类方法FullName()
cout << "Account Number: " << AcctNum() << endl; ---------------------继承的是ABC基类,因此可以直接调用基类方法AcctNum()
cout << "Balance: $" << Balance() << endl; ---------------------继承的是ABC基类,因此可以直接调用基类方法Balance()
Restore(f);
}
// BrassPlus Methods
BrassPlus::BrassPlus(const string & s, long an, double bal,
double ml, double r) : AcctABC(s, an, bal)
{
maxLoan = ml;
owesBank = 0.0;
rate = r;
}
BrassPlus::BrassPlus(const Brass & ba, double ml, double r)
: AcctABC(ba) // uses implicit copy constructor
{
maxLoan = ml;
owesBank = 0.0;
rate = r;
}
void BrassPlus::ViewAcct() const
{
Formatting f = SetFormat();
cout << "BrassPlus Client: " << FullName() << endl;
cout << "Account Number: " << AcctNum() << endl;
cout << "Balance: $" << Balance() << endl;
cout << "Maximum loan: $" << maxLoan << endl;
cout << "Owed to bank: $" << owesBank << endl;
cout.precision(3);
cout << "Loan Rate: " << 100 * rate << "%\n";
Restore(f);
}
void BrassPlus::Withdraw(double amt)
{
Formatting f = SetFormat();
double bal = Balance();
if (amt <= bal)
AcctABC::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);
AcctABC::Withdraw(amt);
}
else
cout << "Credit limit exceeded. Transaction cancelled.\n";
Restore(f);
}
¶
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
|
总结: (0)ABC基类中的纯虚函数声明格式:
C++ | |
---|---|
1 |
|
将共有元素、方法集中包装在一起,方便后续基于此进行个性化定制(抽象类 -> 具体类)
(2)派生类继承ABC基类时:
【1】非“纯虚函数”继承:类中声明不需要virtual,定义格式同正常继承
【2】纯虚函数”继承:类中声明需要virtual,定义格式也同正常继承
C++ 1 2 3 4 5 6 7 8void Brass::ViewAcct() const ---------------------------------------------派生类1的某成员函数(虚函数) { Formatting f = SetFormat(); cout << "Brass Client: " << FullName() << endl; ---------------------继承的是ABC基类,因此可以直接调用基类方法FullName() cout << "Account Number: " << AcctNum() << endl; ---------------------继承的是ABC基类,因此可以直接调用基类方法AcctNum() cout << "Balance: $" << Balance() << endl; ---------------------继承的是ABC基类,因此可以直接调用基类方法Balance() Restore(f); }
(3)进行 ABC——具体类(1,2,3...) 的步骤与原则【参见上面的实例!】:
[1]设计ABC基类
【1】纯虚函数记得写好声明virtual ... = 0;类外定义时不需要virtual 【2】其余函数正常声明与定义,与正常基类没区别
[2]设计具体类(ABC---concrete)
【1】纯虚函数”继承:类中声明需要virtual,定义格式也同正常继承 【2】非“纯虚函数”继承:类中声明不需要virtual,定义格式同正常继承 【3】自定义新函数方法:与正常基类没区别
(4)ABC理念: ![[Pasted image 20230804120546.png]]
6)继承与动态内存分配¶
![[Pasted image 20230804140015.png]]
(1)派生类不使用new¶
从baseDMA类中派生出lacksDMA类,如果后者不使用new,也没特殊设计,就不需要为lackDMA类设计:显式析构函数+复制构造函数+赋值运算符
(2)派生类使用new¶
从baseDMA类中派生出lacksDMA类,如果后者使用new,或者有特殊设计,需要为lackDMA类设计:显式析构函数+复制构造函数+赋值运算符
假如派生类使用了new:
C++ | |
---|---|
1 2 3 4 5 6 7 |
|
【1】析构函数
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 |
|
【2】复制构造函数
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
【3】赋值运算符
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
7)类设计回顾¶
略!