多态的概念

多态的分类

静态多态:函数重载和运算符重载属于静态多态,复用函数名
动态多态:派生类和虚函数实现运行时多态

静态多态和动态多态区别:
静态多态的函数地址早绑定 - 编译阶段确定函数地址
动态多态的函数地址晚绑定 - 运行阶段确定函数地址

案例:
(1)无虚函数

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
class Father
{
public:
void speak()
{
std::cout << "父亲说话" << std::endl;
}
};
class Son1:public Father
{
public:
void speak()
{
std::cout << "儿子1说话" << std::endl;
}
};
//执行说话的函数
void doSpeak(Father &f)//参数为谁的对象,调用谁的函数
{
f.speak();
}//地址早绑定,编译阶段确定函数地址
int main()
{
std::cout << sizeof(Father) << std::endl;//1
Son1 s1;
doSpeak(s1);//父亲说话
return 0;
}

(2)虚函数

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
class Father
{
public:
virtual void speak()//虚函数
{
std::cout << "父亲说话" << std::endl;
}
};
class Son1:public Father
{
public:
void speak ()override//子类继承父类的虚函数,子类的函数也是虚函数
{
std::cout << "儿子1说话" << std::endl;
}
};
class Son2:public Father
{
public:
void speak ()override
{
std::cout << "儿子2说话" << std::endl;
}
};
class Sun1:public Son1
{
public:
void speak()override//孙类继承子类的虚函数,孙类的函数也是虚函数
{
std::cout << "孙子1说话" << std::endl;
}
};
//执行说话的函数
void doSpeak(Father &f)//参数为谁的对象,调用谁的函数
{
f.speak();
}//函数地址晚绑定,运行阶段确定函数地址
int main()
{
std::cout << sizeof(Father) << std::endl;//8
Son1 s1;
doSpeak(s1);
Father f;
doSpeak(f);
Son2 s2;
doSpeak(s2);
Sun1 su1;
doSpeak(su1);
return 0;
}

动态多态的满足条件:(1)有继承的关系(2)子类重写父类的虚函数

重写:子类继承父类后,函数返回值类型、函数名、参数列表完全相同;
重载:同一个类中,函数名相同,参数列表不同的几个函数,与返回值无关。//同名不同参,是重载

动态多态的使用:父类的指针或者引用,指向子类对象

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
class Father
{
private:
std::string name;
int age;
public:
Father():name{},age{}{}
Father(std::string name,int age):name{name},age{age}{}
void setName(std::string name)
{
this->name = name;
}
std::string getName() const
{
return name;
}
void setAge(int age)
{
this->age = age;
}
int getAge() const
{
return age;
}
virtual void desc()
{
std::cout << name << age << std::endl;
}
};
//大儿子已经打工所以有薪水
class Son1:public Father
{
private:
double salary;
public:
Son1():salary{},Father{}{}
Son1(std::string name,int age,double salary):salary{salary},Father{name,age}{}
double getSalary()const
{
return salary;
}
void setSalary(double salary)
{
this->salary = salary;
}
void desc()override
{
std::cout << getName() << getAge() << salary << std::endl;
}

};
//二儿子还是个学生所以有成绩
class Son2:public Father
{
private:
double score;
public:
Son2():score{},Father{}{}
Son2(std::string name,int age,double score):score{score},Father{name,age}{}
double getScore()const
{
return score;
}
void setScore(double score)
{
this->score = score;
}
void desc()override
{
std::cout << getName() << getAge() << score << std::endl;
}
};
void getDesc(Father &f)
{
f.desc();//产生多态:父类的指针或者引用,指向子类对象
}
int main()
{
Father f{"zs",50};
Son1 s1{"z1",30,7000};
Son2 s2{"z2",16,80};
getDesc(f);
getDesc(s1);
getDesc(s2);
return 0;
}

多态原理

案例:
计算器

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
class AbstractCalculator
{
public:
int num1;
int num2;
virtual int getResult()
{
return 0;
}
};
class Add:public AbstractCalculator
{
public:
int getResult()override
{
return num1 + num2;
}
};
class Sub:public AbstractCalculator
{
public:
int getResult()override
{
return num1 - num2;
}
};
class Mul:public AbstractCalculator
{
public:
int getResult()override
{
return num1 * num2;
}
};
int main()
{
AbstractCalculator *abc = new Add;
abc->num1 = 10;
abc->num2 = 20;
std::cout << abc->getResult() << std::endl;
}

纯虚函数和抽象类

在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内
因此可以将虚函数改为纯虚函数

1
格式: virtual 返回值类型 函数名(参数列表)= 0;

当类中有了纯虚函数,这个类也称为 抽象类
纯虚函数一般不用实现,但如果提供实现的话,必须在类外定义

抽象类特点:
(1)无法实例化对象,并不意味着他没有对象,他的对象包括在子类里面
(2)子类必须重写抽象类中的纯虚函数,否则子类也属于抽象类

1
2
3
4
5
6
7
class AbstractCalculator
{
public:
int num1;
int num2;
virtual int getResult()=0;//非虚函数不能=0
};

每个子类对象在构造过程中,必优先构造父类的对象,构造父类的对象必先调用父类的构造函数

案例
制作饮品

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
class AbstractDrinking
{//抽象类
public:
//烧水
virtual void boil() = 0;
//冲泡
virtual void brew() = 0;
//倒入杯中
virtual void pourInCup() = 0;
//加入辅料
virtual void putSomething() = 0;
//整合流程
void markDirnk()
{
boil();
brew();
pourInCup();
putSomething();
}
virtual ~AbstractDrinking(){}
};
class Coffee:public AbstractDrinking
{
public:
virtual void boil()override
{
std::cout << "煮山泉" << std::endl;
}
virtual void brew()override
{
std::cout << "冲咖啡" << std::endl;
}
virtual void pourInCup()override
{
std::cout << "将咖啡倒入杯中" << std::endl;
}
virtual void putSomething()override
{
std::cout << "加糖加奶" << std::endl;
}
};
class Tea:public AbstractDrinking
{
public:
virtual void boil()override
{
std::cout << "煮井水" << std::endl;
}
virtual void brew()override
{
std::cout << "冲菊花茶" << std::endl;
}
virtual void pourInCup()override
{
std::cout << "将茶水倒入杯中" << std::endl;
}
virtual void putSomething()override
{
std::cout << "加枸杞" << std::endl;
}
};
//业务函数
void doWork(AbstractDrinking& drink)
{
drink.markDirnk();
}
int main()
{
Coffee cof;
doWork(cof);
Tea t;
doWork(t);
return 0;
}

虚析构和纯虚析构

多态使用时,如果父类析构函数不是虚拟的,那么在对象回收时,父类指针在释放时无法调用到子类的析构代码

解决方式:将父类中的析构函数改为虚析构或者纯虚析构

1
2
3
4
虚析构语法: virtual ~类名(){}
纯虚析构语法: virtual ~类名() = 0;
类名::~类名(){}

什么时候需要把父类的析构函数变为纯虚析构:我这类本身没有纯虚函数,但是想让这个类成为抽象类

纯虚析构函数的实现通常在类的定义外部提供,以确保派生类在实现自己的析构函数时能够调用基类的析构函数。这样做是为了保证在销毁派生类对象时能正确地执行基类和派生类的清理操作。

案例
电脑组装

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
class CPU
{//抽象类
public:
virtual void calculate() = 0;
virtual ~CPU(){}
};
class VideoCard
{
public:
virtual void display() = 0;
virtual ~VideoCard(){}
};
class Memory
{
public:
virtual void storage() = 0;
virtual ~Memory(){}
};
//Intel厂商
class IntelCPU:public CPU
{//抽象类
public:
virtual void calculate()
{
std::cout << "IntelCPU开始计算" << std::endl;
}
};
class IntelVideoCard:public VideoCard
{
public:
virtual void display()
{
std::cout << "Intel显卡开始显示" << std::endl;
}
};
class IntelMemory:public Memory
{
public:
virtual void storage()
{
std::cout << "Intel内存条开始存储" << std::endl;
}
};

//Lenovo厂商
class LenovoCPU:public CPU
{//抽象类
public:
virtual void calculate()
{
std::cout << "LenovoCPU开始计算" << std::endl;
}
};
class LenovoVideoCard:public VideoCard
{
public:
virtual void display()
{
std::cout << "Lenovo显卡开始显示" << std::endl;
}
};
class LenovoMemory:public Memory
{
public:
virtual void storage()
{
std::cout << "Lenovo内存条开始存储" << std::endl;
}
};
//电脑类
class Commputer
{
private:
CPU* m_cpu;//CPU的零件指针
VideoCard* m_mv;//显卡零件指针
Memory* m_mem;//内存条零件指针
public:
Commputer(CPU* cpu,VideoCard* mv,Memory* mem)
{
m_cpu = cpu;
m_mv = mv;
m_mem = mem;
}
//提供工作函数
void work()
{
//让零件工作起来,调用接口
m_cpu->calculate();
m_mv->display();
m_mem->storage();
}
//提供析构函数,释放三个电脑零件
~Commputer()
{
//释放CPU零件
if(m_cpu != nullptr)
{
delete m_cpu;
m_cpu = nullptr;
}
//释放显卡零件
if(m_mv != nullptr)
{
delete m_mv;
m_mv = nullptr;
}
//释放内存条零件
if(m_mem != nullptr)
{
delete m_mem;
m_mem = nullptr;
}
}
};
int main()
{
//全Intel零件
CPU* intelCPU = new IntelCPU;
VideoCard* intelCard = new IntelVideoCard;
Memory* intelMemory = new IntelMemory;
//创建第一台电脑
Commputer* comm1 = new Commputer(intelCPU,intelCard,intelMemory);
comm1->work();
delete comm1;
//第二台
Commputer* comm2 = new Commputer(new LenovoCPU,new LenovoVideoCard,new LenovoMemory);
comm2->work();
delete comm2;

return 0;
}