多态的概念
多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。
举个栗子:比如买票这个行为,当普通人买票时,是全价买票;学生买票时,是半价买票;军人买票时是优先买票。
多态的定义及实现
1、多态定义的构成条件
多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。 比如Student继承了Person,Person对象买票全价,Student买票半价。
那么在继承中要构成多态还有两个条件:
- 调用函数的对象必须是指针或者引用。
- 被调用的函数必须是虚函数,且完成了虚函数的重写。
什么是虚函数?
虚函数:就是在类的成员函数的前面加virtual关键字。
class Person
{
public:
virtual void BuyTicket()
{
cout << "买票-全价" << endl;
}
};
注:在基类中的成员函数必须加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();
}
int main()
{
Person ps;
Student st;
Func(ps);
Func(st);
return 0;
}
虚函数的例外:协变
虚函数重写有一个例外:重写的虚函数的返回值可以不同,但是必须分别是基类指针和派生类指针或者基类引用和派生类引用。(了解一下即可,这个用的很少)
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; }
};
析构函数的重写问题
基类中的析构函数如果是虚函数,那么派生类的析构函数就重写了基类的析构函数。这里他们的函数名不相同,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor,这也说明基类的析构函数最好写成虚函数。
class Person
{
public:
virtual ~Person() { cout << "~Person()" << endl; }
};
class Student : public Person
{
public:
virtual ~Student() { cout << "~Student()" << endl; }
};
//只有派生类Student的析构函数重写了Person的析构函数,下面的delete对象调用析构函数,
//才能构成多态,才能保证p1和p2指向的对象正确的调用析构函数。
int main()
{
Person* p1 = new Person;
Person* p2 = new Student;
delete p1;
delete p2;
return 0;
}
接口继承和实现继承
普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承但是函数的实现。虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成多态,继承是接口。所以如果不实现多态,不要把函数定义成虚函数。
2、重载、覆盖(重写)、隐藏(重定义)的对比
抽象类
在虚函数的后面写上=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();
Car c; //编译不通过,因为Car是抽象类,无法实例化出对象
}
C++11中的override和final
C++11提供override和final来修饰虚函数。
实际中我们建议多使用 纯虚函数 + override 的方式来强制重写虚函数,因为虚函数的意义就是实现多态,如果没有重写,虚函数就没有意义。
//1、final修饰基类的虚函数不能被派生类填写
class Car
{
public:
virtual void Drive() final {}
};
class Benz : public Car
{
public:
//无法完成重写
virtual void Drive() { cout << "Benz-舒适" << endl; }
};
class Car
{
public:
virtual void Drive(){}
};
//2、override 修饰派生类虚函数强制完成重写,如果没有重写会编译报错
class Benz : public Car
{
public:
virtual void Drive() override { cout << "Benz-舒适" << endl; }
};
class Car
{
public:
virtual void Drive(){}
};
//2、override 修饰派生类虚函数强制完成重写,如果没有重写会编译报错
class Benz : public Car
{
public:
virtual void Drive() override { cout << "Benz-舒适" << endl; }
};