一、什么是多态
多态:是在不同的继承关系的类对象,调用同一函数,产生不同的行为。
多态产生的两个条件:
1、必须通过基类的指针或者引用调用虚函数
2、被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写
虚函数
虚函数:即被virtual修饰的函数叫做虚函数
二、重写:
C++中的多态性是通过虚函数的重写实现的,虚函数允许派生类重新定义成员函数,派生类重新定义基类中函数的做法叫做重写或者覆盖。
需要注意的是:在派生类中重写基类的虚函数的时候,派生类的虚函数前可以不加virtual关键字,虽然能够构成重写(因为继承后基类的虚函数被继承了下来在派生类依然具有虚函数的属性),但是不建议这样使用,不太规范。
重写的条件:
- 在派生类中重写的函数在基类中必须是虚函数
- 派生类的虚函数必须与基类中的虚函数原型保持一致(函数名,参数,返回值类型都必须相同)
- 两个函数的作用域不同,分别在基类和派生类当中
- 特例:协变和析构函数的重写。析构函数函数名不同,但是仍然可以进行重写;协变:基类虚函数返回基类的指针或引用,派生类虚函数返回派生类的指针或者引用,基类与派生类返回类型不同。
代码解释协变
class A{
};
class B : public A
{
};
class person
{
public:
virtual A* fun()
{
return new A;//在基类中返回基类的指针或者引用
}
};
class people : public person
{
public:
virtual B* fun()
{
return new B;//在派生类中返回派生类的指针或引用
}
};
析构函数的重写
虽然函数名不相同,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理
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;
}
注意:只有派生类Student的析构函数重写了Person的析构函数,下面的delete对象调用析构函数,才能 构成多态,才能保证p1和p2指向的对象正确的调用析构函数。
C++对函数重写的要求比较严格,但是有些情况下由于疏忽,可能会导致函数名字母次序 写反而无法构成重载,而这种错误在编译期间是不会报出的,只有在程序运行时没有得到预期结果才来 debug会得不偿失,因此:C++11提供了override和final两个关键字,可以帮助用户检测是否重写
final:修饰虚函数表示虚函数不能再被继承
override 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错
三、抽象类:
首先了解什么是纯虚函数:
在虚函数的后面写上 =0 ,则这个函数为纯虚函数。
抽象类:包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。派生类继承后也不能实例化处对象,只有重写虚函数,派生类才能实例化出对象。纯 虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。
代码解释:
class animal
{
public:
virtual void function() = 0;
};
class cat :public animal
{
public:
virtual void function()
{
cout << "捉老鼠" << endl;
}
};
class dog :public animal
{
public:
virtual void function()
{
cout << "看家" << endl;
}
};
int main()
{
//animal* ac = new animal;//不能实例化出对象
//需要重写虚函数才能,派生类才能实例化出对象
animal* ac = new cat;
ac->function();
animal* ad = new dog;
ad->function();
system("pause");
return 0;
}
抽象类不能实例化出对象
四、重写(覆盖)、重载、隐藏(重定义)的对比
重载:
- 两个函数在同一作用域
- 函数名相同,参数也相同
重写:
- 两个函数分别在基类和派生类的作用域
- 函数名必须相同、参数必须相同、返回值必须相同(协变例外)
- 两个函数必须都是虚函数
重定义:
- 两个函数分别在基类和派生类的作用域
- 函数名相同
- 两个基类和派生类的函数不构成重写就是重定义