一.多态的理解
1.定义
定义:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。
多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。
条件:1. 必须通过基类的指针或者引用调用虚函数 2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写
原因解释:多态是在继承的基础上实现的,子类(派生类)和父类(基类)之间满足切片原则,即可以通过父类(基类)的指针或者引用去调用虚函数
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.重写 / 重载 / 覆盖
构成多态的第二个条件就是要构成虚函数的重写,虚函数重写的含义是在继承的基础上在父类上加virtual,在父类上加virtual可以不在子类上加virtual,因为继承后的析构函数也要写成虚函数的形式,仅在父类上加virtual就可以完成析构函数的重写
概念区分:1.重载:函数重载的必要条件就是必须要在同一个作用域,函数重载的实现使得程序中可以出现多个函数名相同函数,其中要求两个 / 多个函数的函数名 / 参数(参数指数量,参数类型和顺序可以不同(面试常考: 返回值不同不构成函数重载!!!)— 例: int func() / void func()不构成函数重载
条件:同一作用域,函数名 / 参数相同
重载是在同一作用域下,函数名相同情况下,通过改变函数的参数来实现的,参数的数量 / 参数的类型 / 相同参数下变量的类型顺序
2.函数重写(覆盖):函数重写是多态的重要条件,virtual是关键词,如果不是按照父类调用子类的条件,那么父类中的函数回直接将子类的函数覆盖,用子类 / 父类调用都是只是父类的函数
条件:两个函数分别在父类和子类的作用域,函数名 / 参数 / 返回值都必须相同 (协变例外)/ 两个函数都必须是虚函数(仅父类加virtual即可)
3.重定义(隐藏):重写和重定义类似,但是两个函数不在同一个作用域,所以不构成重写,重定义存在的意义也是使得继承类中存在同名的函数,但是重定义的条件较少,仅要求函数名相同即可(不需要管参数和返回值)
条件:两个函数分别在父类和子类的作用域, 函数名相同, 当两个在父类和子类的同名函数,不构成重写就是重定义
3.虚函数重写的两个例外
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即可,子类可以不加virtual,就是为析构函数开了一个通路
如果父类的析构函数为虚函数,此时子类析构函数只要定义,无论是否加virtual关键字,都与父类的析构函数构成重写,虽然父类与子类析构函数名字不同。虽然函数名不相同,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor,为了防止析构函数无法构成重写,这里进行了特殊处理,仅在父类加virtual即可
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;
}
4.虚函数两个关键字
1.final:修饰虚函数,表示该虚函数不能再被重写
class A
{
public:
virtual void func() final
{};
};
class B : public A
{
void func() // Error
{};
};
2.override:检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错
class A
{
public:
virtual void func()
{};
};
class B : public A
{
void func() override
{};
};
5.抽象类
。。。未完