28.C++多态1 (多态的概念与简单使用,虚函数,final,override)
⭐上篇文章:27.C++继承 3 (复杂的菱形继承与菱形虚拟继承)-CSDN博客
⭐本篇代码:c++学习/17.C++三大特性-多态 · 橘子真甜/c++-learning-of-yzc - 码云 - 开源中国 (gitee.com)
⭐标⭐是比较重要的部分
目录
一. C++多态简介
1.1 构成多态的两个必要条件
二. virtual关键字与虚函数 ⭐
1.1 使用虚函数测试多态
1.2 virtual关键字的用法
三. 虚函数重写的两个例外
3.1 协变
3.2 析构函数 ⭐
四. final 和 override关键字
4.1 final
4.2 override
一. C++多态简介
多态是指不同有继承关系的对象在调用同一个函数之后,会有不同的结果。比如学生票和成人票。
Student继承了Person,但是Studenet买票是半价,而Person是全价
1.1 构成多态的两个必要条件
1 必须通过基类的指针或者引用去调用虚函数
2 被调用的函数必须是虚函数,且派生类必须完成对虚函数的重写
二. virtual关键字与虚函数 ⭐
用virtual修饰的成员函数就是虚函数
比如:下面代码中的buyTickets就是虚函数
#include <iostream>
using namespace std;
class Person
{
public:
virtual void buyTickets()
{
cout << "全价买票" << endl;
}
};
int main()
{
}
1.1 使用虚函数测试多态
#include <iostream>
using namespace std;
class Person
{
public:
virtual void buyTickets()
{
cout << "全价买票" << endl;
}
};
class Student : public Person
{
public:
virtual void buyTickets()
{
cout << "半价买票" << endl;
}
};
void test(Person& tmp)
{
tmp.buyTickets();
}
int main()
{
Person p1;
Student s1;
Person* tmp1 = &p1;
Student* tmp2 = &s1;
//测试指针
cout << "使用指针完成多态" << endl;
tmp1->buyTickets();
tmp2->buyTickets();
//测试引用
cout << "使用引用完成多态" << endl;
test(p1);
test(s1);
return 0;
}
测试结果如下:
可以看到,我们使用基类的指针或者引用成功的调用了基类和派生类的函数,从而达到一种多态的效果
1 如果两个有继承关系的成员函数满足多态的条件,指针指向哪个对象或者引用哪个对象就调用这个对象的成员函数
2 如果不满足多态条件,调用的类型对象是谁,就调用该对象的成员函数
3 重写的虚函数需要保持返回值类型,名称,参数列表都一致
1.2 virtual关键字的用法
1 virtual修饰类的继承中,完成虚继承,解决菱形继承中的冗余性和二义性
2 virtual修饰成员函数,变为虚函数完成函数的重写
三. 虚函数重写的两个例外
3.1 协变
如果派生类重写虚函数的时候,两个函数的返回值类型不同,但是都为本身类的指针或者引用的时候。也构成虚函数的重写
如下面的代码,基类返回基类的指针,派生类返回派生类的指针
#include <iostream>
using namespace std;
class Person
{
public:
virtual Person* buyTickets()
{
cout << "全价买票" << endl;
return this;
}
};
class Student : public Person
{
public:
virtual Student* buyTickets()
{
cout << "半价买票" << endl;
return this;
}
};
void test(Person& tmp)
{
tmp.buyTickets();
}
int main()
{
Person p1;
Student s1;
Person* tmp1 = &p1;
Student* tmp2 = &s1;
//测试指针
cout << "使用指针完成多态" << endl;
tmp1->buyTickets();
tmp2->buyTickets();
//测试引用
cout << "使用引用完成多态" << endl;
test(p1);
test(s1);
return 0;
}
3.2 析构函数 ⭐
如果有继承关系,并且我们需要使用多态的时候。需要使用virtual修饰析构函数,将其定义为虚函数。
否则当我们使用基类的指针或者引用之后,销毁这个变量的时候就会由于隐藏只调用当前对象的析构函数。
比如:
#include <iostream>
using namespace std;
class A
{
public:
virtual void test(){}
~A() { cout << " ~A()" << endl; }
};
class B :public A
{
public:
virtual void test() {}
~B() { cout << " ~B()" << endl; }
};
int main()
{
//使用多态
A* a = new A();
A* b = new B();
a->test();
b->test();
//删除变量
delete a;
delete b;
return 0;
}
在上面的代码中,我们使用多态去调用test。然后使用delete删除变量。正常来说,析构函数的调用顺序是 ~A () ~B() ~A()
测试结果如下:
可以看到,只调用了A的析构函数。这是因为我们没有对析构函数进行虚函数重写,导致B类的析构函数和A类的析构函数构成隐藏,又由于我们的指针定义的时候是A,所以不会调用B的析构函数,从而导致内存泄漏
对析构函数进行重写
#include <iostream>
using namespace std;
class A
{
public:
virtual void test(){}
virtual ~A() { cout << " ~A()" << endl; }
};
class B :public A
{
public:
virtual void test() {}
virtual ~B() { cout << " ~B()" << endl; }
};
int main()
{
//使用多态
A* a = new A();
A* b = new B();
a->test();
b->test();
//删除变量
delete a;
delete b;
return 0;
}
可以看到,成功的调用了B的析构函数。解决了内存泄漏问题
四. final 和 override关键字
4.1 final
final有两个用法
final修饰类,这个类不能被继承。
final修饰成员函数,这个成员函数不能被重写
修饰虚函数
修饰类
4.2 override
override修饰成员函数,会检查这个成员函数是否重写了父类的函数,如果不构成重写,就会显示报错
举例:
#include <iostream>
using namespace std;
class A
{
public:
virtual void test()
{}
void f(){}
};
class B :public A
{
public:
virtual void test() override
{}
virtual void f()override
};
int main()
{
return 0;
}
无法通过编译