0%

C++ 多态

多态

多态是面向对象的非常重要的一个特性
同一操作可以作用于不同对象,可以有不同的解释,产生不同的执行效果
在运行时,可以识别出真正的对象类型,调用对应子类中的函数
多态的要素:

  1. 子类重写父类的成员函数(override)
  2. 父类指针指向子类对象
  3. 利用父类指针调用重写的成员函数

背景

先看一个情况,当一个函数需要传不同的对象参数时,用重载函数写可扩展性差,太麻烦

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
struct Dog {
void speak() {
cout << "dog::speak" << endl;
}
void run() {
cout << "dog::run" << endl;
}
};
struct Cat {
void speak() {
cout << "cat::speak" << endl;
}
void run() {
cout << "cat::run" << endl;
}
};
struct Pig {
void speak() {
cout << "pig::speak" << endl;
}
void run() {
cout << "pig::run" << endl;
}
};

void liu(Dog* p) {
p->run();
p->speak();
}
void liu(Cat* p) {
p->run();
p->speak();
}
void liu(Pig* p) {
p->run();
p->speak();
}

int main() {
liu(new Dog());
liu(new Cat());
liu(new Pig());
return 0;
}

默认情况下,编译器只会根据指针类型调用对应的函数,不存在多态

1
2
3
4
5
6
7
Animal* p = new Dog();
p->speak();
p->run();
/*输出:
animal::speak
animal::run
*/
1
2
3
4
5
6
7
Cat* p = (Cat*)new Dog();
p->speak();
p->run();
/*
cat::speak
cat::run
*/

以上就很离谱,反汇编查看,直接调用cat::speak

虚函数

C++中多态通过虚函数实现
虚函数:被 virtual 修饰的成员函数
只要在父类中声明为虚函数,子类中重写的函数也自动变成虚函数(子类中可以省略virtual关键字)

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
struct Animal {
virtual void speak() {
cout << "animal::speak" << endl;
}
virtual void run() {
cout << "animal::run" << endl;
}
};

struct Dog : Animal{
//重写、覆盖、覆写
void speak() {
cout << "dog::speak" << endl;
}
void run() {
cout << "dog::run" << endl;
}
};
struct Cat : Animal{
void speak() {
cout << "cat::speak" << endl;
}
void run() {
cout << "cat::run" << endl;
}
};
struct Pig : Animal{
void speak() {
cout << "pig::speak" << endl;
}
void run() {
cout << "pig::run" << endl;
}
};

void liu(Animal* p) {
p->run();
p->speak();
}

int main() {
liu(new Dog());
liu(new Cat());
liu(new Pig());
/*Cat* p = (Cat*)new Dog();
p->speak();
p->run(); */
return 0;
}

虚表

虚函数的实现原理是虚表,这个虚表里面存储的最后需要调用的虚函数地址,这个虚表也叫虚函数表

汇编分析:

虚析构函数

如果有父类指针指向子类对象时,应该将析构函数声明为虚函数(虚析构函数)
delete父类指针时,才会调用子类,保证析构的完整性

纯虚函数 & 抽象类

没有函数体且初始化为0的虚函数,用来定义接口规范

1
2
3
4
struct Animal {
virtual void speak() = 0;
virtual void run() = 0;
};

含有纯虚函数的类,叫做抽象类

  1. 不可以实例化(不可以创建对象)
  2. 抽象类也可以包括非纯虚函数、成员变量
  3. 如果父类是抽象类,子类没有完全重写纯虚函数,那么子类依然是个抽象类
求大佬赏个饭