前言
C
目录
- 1. 多态的概念
- 2 多态的定义及实现
- 2 .1 虚函数:
- 2 .2 虚函数的重写:
- 2 .2.1 虚函数重写的两个例外:
- 2 .3 多态的两个条件(重点)
- 2 .4 析构函数为啥写成虚函数
- 3 新增的两个关键字
- 3.1 final的使用:
- 3.2 override :
- 4 抽象类
- 4.1 概念
- 5 多态的原理
1. 多态的概念
通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态. 一个接口,多种行为。
重写其实是一种接口继承
2 多态的定义及实现
2 .1 虚函数:
虚函数: 即被 virtual 修饰的类成员函数称为虚函数。
class Person
{
public:
virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
2 .2 虚函数的重写:
虚函数的重写(覆盖):
- 派生类中有一个跟基类完全相同的虚函数
- 即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同(三同)
- 称子类的虚函数(重写)了基类的虚函数,也叫(覆盖)
不符合重写,就构成隐藏。。。
class Person {
public:
virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:
virtual void BuyTicket() { cout << "买票-半价" << endl; }
/*注意:子类虚函数不加Virtual,依旧构成重写(实际最好加上)*/
2 .2.1 虚函数重写的两个例外:
- 协变—基类与派生类虚函数返回值类型不同:
派生类重写基类虚函数时,与基类虚函数返回值类型不同。
即基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变
- [ ] 协变的返回值类型可以不同,要求必须是(父子关系)的指针和引用
class A{};
class B : public A {};
class Person {
public:
virtual A* f() {return new A;}
};
class Student : public Person {
public:
virtual B* f() {return new B;}
};
2.析构函数的重写
- 如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加virtual关键字,都与基类的析构函数构成重写,虽然基类与派生类析构函数名字不同
- 虽然函数名不相同,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor
class Person {
public:
virtual ~Person() {cout << "~Person()" << endl;}
};
class Student : public Person {
public:
virtual ~Student() { cout << "~Student()" << endl; }
};
2 .3 多态的两个条件(重点)
- 必须通过基类的指针或者引用调用虚函数
- 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写
2 .4 析构函数为啥写成虚函数
1.析构函数不构成多态的情况:
在这里,析构函数默认是隐藏关系
class Person {
public:
~ Person ()
{
cout << "~ Person( )" << endl;
}
};
class Student : public Person {
public:
~ Student ()
{
cout << "~ Student ( )" << endl;
}
};
2.析构函数构成多态:
class Person {
public:
virtual ~ Person ()
{
cout << "~ Person( )" << endl;
}
};
class Student : public Person {
public:
virtual ~ Student ()
{
cout << "~ Student ( )" << endl;
}
};
其析构函数最好定义为虚函数。见下面的例子:
- 指向谁,就调用谁析构。
3 新增的两个关键字
- C++11提供了override和final两个关键字,可以帮助用户检测是否重写。
3.1 final的使用:
final:修饰虚函数,表示该虚函数不能再被重写。
class Car
{
public:
virtual void Drive() final {}
};
class Benz :public Car
{
public:
virtual void Drive() {cout << "Benz-舒适" << endl;}
};
3.2 override :
检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错
class Car{
public:
virtual void Drive(){}
};
class Benz :public Car {
public:
virtual void Drive() override {cout << "Benz-舒适" << endl;}
};
重载、覆盖(重写)、隐藏(重定义)的对比
- 同名的成员变量也是隐藏的关系
4 抽象类
4.1 概念
- 在虚函数的后面写上 =0 ,则这个函数为纯虚函数。
- 包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。
- 派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。
- 纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承
class Car
{
public:
virtual void Drive() = 0;
};
class Benz :public Car
{
public:
virtual void Drive()
{
cout << "Benz-舒适" << endl;
}
};
5 多态的原理
class Base
{
public:
virtual void Func1()
{
cout << "Func1()" << endl;
}
private:
int _b = 1;
};
通过观察测试我们发现b对象是8bytes,除了_b成员,还多一个__vfptr放在对象的前面(注意有些
平台可能会放到对象的最后面,这个跟平台有关),对象中的这个指针我们叫做虚函数表指针
- 普通函数调用,编译链接时确定函数的地址,运行时直接调用。
- **多态调用,**程序运行时去指向对象的虚表中找到函数的地址,进行调用。
总结:
多态的本质原理,当符合多态的两个条件时,
那么调用时,会到指向对象的虚表中找到对应的虚函数地址,进行调用
尾声
看到这里,相信大家对这个C++有了解了。
如果你感觉这篇博客对你有帮助,不要忘了一键三连哦