9-1:C++多态之对多态的理解和多态的实现条件以及虚函数还有重载重写冲定义的区别
Posted 快乐江湖
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了9-1:C++多态之对多态的理解和多态的实现条件以及虚函数还有重载重写冲定义的区别相关的知识,希望对你有一定的参考价值。
文章目录
(1)对于多态的理解
所谓多态,就是指完成某个行为时,不同对象有有不同的完成方法
比如经典的买票行为,学生和普通人都属于人,但是学生买票可以半价,而普通人则只能全票
(2)多态的实现
A:实现条件
用下面的代码为例讲解如何实现多态
#include <iostream>
using namespace std;
class Person
{
public:
void BuyTicket()
{
cout << "全价" << endl;
}
};
class Student : public Person
{
public:
void BuyTicket()
{
cout << "半价" << endl;
}
};
void fun(Person& p)
{
p.BuyTicket();
}
int main()
{
Person Job;
fun(Job);
Student Bob;
fun(Bob);
}
由于Student
会把Person
中的函数隐藏,因此输出结果全部是全价
要实现多态必须满足以下条件
- 虚函数:对于某些函数,父类希望它的子类各自定义适合自己的版本,此时父类就将这些函数声明成虚函数
因此修改上述代码如下
#include <iostream>
using namespace std;
class Person
{
public:
virtual void BuyTicket()
{
cout << "全价" << endl;
}
};
class Student : public Person
{
public:
virtual void BuyTicket()
{
cout << "半价" << endl;
}
};
void fun(Person& p)
{
p.BuyTicket();
}
B:虚函数的重写
虚函数的重写:子类中有一个和父类完全相同的虚函数(返回值类型,函数名字,参数列表完全相同),则称子类的虚函数重写了父类的虚函数
- 需要注意的是如果子类不写
virtua
l实际也是可以构成多态的,这一点是为了后面析构函数的重写而留下来的后门(即如果父类把一个函数声明成虚函数,则该函数在子类中隐式地也是虚函数)
所以在C++中,父类就有两种成员函数:一种是父类希望其子类进行重写的函数,另一种则是父类希望子类直接继承而不要改变的函数。对于前者,父类就把它定义为虚函数,当我们用指针或引用调用虚函数时,就会根据引用或指针所绑定的对象的类型的不同而制定子类的版本或父类的版本
C:虚函数的重写两个例外
1-满足协变:子类与父类的返回值类型不同
- 即父类虚函数返回父类对象的指针或者引用,子类虚函数返回子类对象的指针或者引用时,称为协变
2-析构函数的重写:父类的析构尽可能设置为虚函数
前面说过,编译器会对析构函数做统一处理,全部处理为destructor
。如果父类的析构函数不定义为虚函数,那么子类就不会重写父类的虚函数,因此在下面的这样一种场景中就会产生内存泄漏的问题
下面代码中,分别用父类的指针指向父类对象和子类对象,接着使用delete去释放对象
class Person
{
public:
void BuyTicket()
{
cout << "全价" << endl;
}
~Person()
{
cout << "父类析构函数调用" << endl;
}
};
class Student : public Person
{
public:
void BuyTicket()
{
cout << "半价" << endl;
}
~Student()
{
cout << "子类析构函数调用" << endl;
}
};
void fun(Person& p)
{
p.BuyTicket();
}
int main()
{
Person* p = new Person;
Person* s = new Student;
delete p;
delete s;
}
按照正常情况应该是先调用父类的析构函数,然后再调用子类的析构函数,子类的析构函数调用完成之后又会自动调用父类的析构函数。但是因为父类的析构函数没有设置为虚函数,所以两个析构函数关系为隐藏,这样的话由于指针都是父类的,因此调用时本来应该调用子类的虚构函数又会去调用父类,而子类没有被释放,造成了内存泄漏
父类析构函数如果加入virtual
,不管子类写不写virtual
,子类都会重写父类的析构函数,因此析构函数满足多态,这样调用时就会根据对象进行调用,父类调用父类的,子类调用子类的。
- 所以为了以防出现一些不容易查找的错误,写类的析构函数时都设置为虚函数即可
(3)override和final
A:final
final可以用来修饰虚函数,表明该虚函数不再被继承,其位置和const修饰符出现的位置一致
此外,final也可用来修饰一个类,表明他是一个最终类,不能被继承
B:override
可以发现,函数重写的条件比较严格,而虚函数是在运行时被解析的,这就意味着如果前期比如参数列表写错后,你认为已经重写了,但是编译器没有检查出来,这样去调试得不偿失,所以override会进行重写检查,如果没有重写就报错
(4)重载,重写(覆盖)和重定义(隐藏)的区别
以上是关于9-1:C++多态之对多态的理解和多态的实现条件以及虚函数还有重载重写冲定义的区别的主要内容,如果未能解决你的问题,请参考以下文章
C++ 多态 : 多态的构成条件finaloverride协变析构函数的重写抽象类
C++ 多态 : 多态的构成条件finaloverride协变析构函数的重写抽象类