1、多态的概念
1.1概念引入:
我们在日常去景点、坐火车等地方,都需要买票,而对成年人是要求购买全价票。对学生而言需要购买半价票。这个场景是怎么样实现的呢?怎么同样是一个人,如何能清晰的区分该购买哪一种价格的票呢?
C++中,我们引入多态这个概念,通俗的讲,多态就是多种形态;当去完成某个行为的时候,不同的对象完成会产生不同的结果。
所以,对于买票而言,我们成年人去买,后台会让给我们购买全票,学生去买,只会买半价票。
2、多态的实现
2.1虚函数
class Person {
public:
virtual void BuyTicket() { cout << "买票-全价" << endl;}
};
虚函数的重写
2.2构成条件
2.2.1多态有两个构成的必要条件
class Person {
public:
virtual void BuyTicket()
{
cout << "买票-全价" << endl;
}
};
class Student : public Person {
public:
virtual void BuyTicket() //子类虚函数重写父类虚函数
{
cout << "买票-半价" << endl;
}
};
void Func(Person& p)//基类引用调用虚函数
{
p.BuyTicket();
}
int main()
{
Person ps;
Student st;
Func(ps);//传父类对象调用父类
Func(st);//传子类对象调用子类
//st传给p——切割出子类中父类那部分,去调用子类的虚函数
return 0;
}
另外需要注意,基类虚函数的virtual是不能省略的,省略就不会构成多态派生类的虚函数中virtual可以省略,因为虚函数的重写本质是对基类虚函数实现部分的重写,函数声明会继承下来。
2.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() //基类和派生类虚函数返回类型不一样,但B是A的子类,所以仍然构成多态
{
return new B;
}
};
2.析构函数的重写
class Person {
public:
virtual ~Person()//编译后析构函数名字统一都叫destructor
{
cout << "~Person()" << endl;
}
};
class Student : public Person {
public:
virtual ~Student() //编译后析构函数名字统一都叫destructor
{
cout << "~Student()" << endl;
}
};
int main()
{
Person* p1 = new Person;
Person* p2 = new Student;
delete p1;//p1->~Person();
delete p2;//p2->~Student();
return 0;
}
C++11提供了两个关键字,final和override;
final修饰的类表示该类不能被继承,final修饰的虚函数,表示该虚函数不能被子类重写;
override修饰派生类的虚函数,表示检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。
区分对比函数重载、隐藏、重写
3、抽象类
class Car
{
public:
virtual void Drive() = 0;
};
class Benz :public Car
{
public:
virtual void Drive()
{
cout << "Benz-灯光效果好看,氛围好,舒适" << endl;
}
};
class BMW :public Car
{
public:
virtual void Drive()
{
cout << "BMW-操控" << endl;
}
};
void Test()
{
Car* pBenz = new Benz;
pBenz->Drive();
Car* pBMW = new BMW;
pBMW->Drive();
}
例如这个Car类,车是一个大类,它并不能实例化出什么对象,没有任何意义。这个时候我们在它的虚函数的后面加上=0,让它成为一个抽象类。
抽象类的意义是强制让派生类重写虚函数,因为如果不重写的话,子类依旧是抽象类。
我们上面提到虚函数的重写是对实现部分的重写,所以它是一个接口继承。而我们普通函数继承的是实现,叫实现继承。
4、多态的原理
class Base
{
public:
virtual void Func1()
{
cout << "Func1()" << endl;
}
private:
int _b = 1;
};
int main()
{
cout << sizeof(Base) << endl;
return 0;
}
这个类的大小是多少(32位)下,大家肯定觉得是4字节,其实不然
很多人疑惑,为什么是8呢?其实类里面有虚函数,创建的对象里面还有一个虚表指针_vfptr
那这个是什么多西呢?虚表指针?虚表的地址?虚表又是什么?其实虚表是虚函数数组,这个数组里面存放的是虚函数的地址。因为能力有限,所以我认为我把虚表指针的底层给大家讲不明白,推荐大家自己在老师讲课的时候重点注意这块,或者自己在网上找一些课程研究,实在是对我来是目前还没有掌握透彻,抱歉啦。