多态《二》的链接:https://blog.youkuaiyun.com/dpfxaca6/article/details/89521976
多态《三》的链接:https://blog.youkuaiyun.com/dpfxaca6/article/details/89576789
《一》 多态的概念
概念:通俗来讲,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生不同的状态。
《 二 》多态的定义及实现
(1)多态定义的构成条件
多态是不同继承关系的类和对象,去调用同一个函数,产生不同的的行为,比如:Student继承Preson类,Preson对象买全票,Student买半票。
那么在继承中构建多态的有两种情况
一种是:调用的函数必须是是指针或者引用。
一种是:被调用的函数,必须是虚函数,并且完成了虚函数的重写。
#include<iostream>
using namespace std;
class Preson
{
public:
virtual void BuyTicket()
{
cout << "买全票" << endl;
}
};
class Student :public Preson
{
virtual void BuyTicket()
{
cout << "买半票" << endl;
}
};
void Func(Preson& people)
{
people.BuyTicket();
}
int main()
{
Preson p;
//Func(p);
p.BuyTicket();
Student s;
Func(s);
//使用下面这个会出现错误,因为我们上面使用virtual关键字了,
//s.BuyTicket();
return 0;
}
为什么使用virtual关键,这样就使他变成了 虚函数了 ,就是直接指向它了的基类,解决了二义性的问题,前面我们也说过了。
虚函数是什么?
答:就是我们上面在函数前面写的virtual关键字之后,此函数就是虚函数。
什么是虚函数的重写?
答;派生类中有一个跟基类的完全相同虚函数,我们就称子类的虚函数重写了基类的虚函数,完
全相同是指:函数名、参数、返回值都相同。另外虚函数的重写也叫作虚函数的覆盖。
class Person {
public:
virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:
virtual void BuyTicket() { cout << "买票-半价" << endl; }
};
void Func(Person& p)
{
p.BuyTicket();
}
(2)虚函数的重写例外:协变
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;
}
};
void func(Person& ps)
{
ps.f();
}
int main()
{
Person p;
func(p);
Student s;
func(s);
}
我们把上面的这段代码,放在编译器中,我们可以看到,是没有一点的错误的,所以说这样也是可以的,但是这不是我们正规的写法,所以说,我们一般是使用上面的写法的。
(3)不规范的重写行为
在派生类中重写的成员函数可以不加virtual关键字,也是构成重写的,因为继承后基类的虚函数被继承下来了在派生类依旧保持虚函数属性,我们只是重写了它。但是这是非常不规范的,所以我们一般是不使用这样的写法的
class Person {
public:
virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:
void BuyTicket() { cout << "买票-半价" << endl; }
};
这样写,也是可以的,但是不规范。
(4)析构函数的重写问题
基类中的析构函数如果是虚函数,那么派生类的析构函数就重写了基类的析构函数。这里他们的函数名不相同,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor,这也说明的基类的析构函数最好写成虚函数。
class Person {
public:
virtual ~Person() { cout << "~Person()" << endl; }
};
class Student : public Person {
public:
virtual ~Student() { cout << "~Student()" << endl; }
};
int main()
{
Person* p1 = new Person;
Person* p2 = new Student;
delete p1;
delete p2;
return 0;
}
下面我们看一下执行的代码:
【 绿色表示,他先找到基类,然后再找到派生类,然后析构是,先派生类在基类顺序 】
(5)重载,覆盖(重写),隐藏(重定义)的对比
《三》抽象类
抽象类的定义:在虚函数的后面写上 =0,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。
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();
}
int main()
{
Test();
return 0;
}
这里的这段代码 就说明了 上面的这段描述,如果看上面的文字不太理解,就配合下面的文字一起看。
下面还有一种:
class Car{
public:
virtual void Drive()
{}
};
// 2.override 修饰派生类虚函数强制完成重写
class Benz :public Car {
public:
virtual void Drive() override
{
cout << "Benz-舒适" << endl;
}
};
void Test()
{
Car* pBenz = new Benz;
pBenz->Drive();
//Car* pBMW = new BMW;
//pBMW->Drive();
}
int main()
{
Test();
return 0;
}
实际中我们建议多使用纯虚函数+ overrid的方式来强制重写虚函数,因为虚函数的意义就是实现多态,如果没有重写,虚函数就没有意义。
《四》关键字final (c++11)
final修饰基类的虚函数不能被派生类重写
class Car
{
public:
virtual void Drive() final {}
};
class Benz :public Car
{
public:
virtual void Drive() { cout << “Benz-舒适” << endl; }
};
这里我们看一下程序的图片,就比较好理解
这里的内容有一点多,我在另外写一篇博客,会把链接放在下面的 ,提供方便。