多态
1. 多态的概念
- 多态:多种形态;就是去完成某个行为,当不同的对象去完成时,会产生出不同的状态;
2. 多态的构成条件
- 多态是在不同继承关系的类对象中调用同一函数,产生了不同的行为;
- 必须通过基类的指针或者引用调用虚函数;
- 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写。
实现多态:
- 前提:继承
- 虚函数
- 调用虚函数的类型必须时指针或引用
- 虚函数需要被重写
- 一般是通过父类的指针或引用调用
3. 虚函数
- 虚函数:被virtual修饰的类成员函数。
4. 虚函数的重写
4.1 虚函数的重写(覆盖)
- 派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名、参数列表完全相同)。
class Person
{
public:
//虚函数
virtual void buyTicket()
{
cout << "全价" << endl;
}
};
class Student : public Person
{
public:
//虚函数重写:
//函数名,参数列表,返回值和父类虚函数完全相同
virtual void buyTicket()
{
cout << "半价" << endl;
}
private:
char* _name = new char[100];
};
void fun(Person& rp)
{
rp.buyTicket();
}
void test()
{
Person p;
Student stu;
fun(p);
fun(stu);
}
4.2 协变
- 派生类重写基类虚函数时,基类与派生类虚函数返回值类型不同,即基类虚函数返回基类对象的指针或引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。
class A{};
class B : public A {};
class Person
{
public:
virtual A* Hongbao()
{
cout << "一毛钱" << endl;
return new A;
}
};
class Student : public Person
{
public:
//协变:返回值可以不是同一个类型,但是必须是有继承关系的指针或者引用
virtual B* Hongbao()
{
cout << "100元" << endl;
return new B;
}
private:
char* _name = new char[100];
};
//多态:看对象
void fun(Person& rp)
{
rp.Hongbao();
}
void test()
{
Person p;
Student stu;
fun(p);
fun(stu);
}
4.3 析构函数的重写
- 如果基类的析构函数为虚函数,此时派生类虚函数只要定义,无论是否加virtual关键字,都与基类的析构函数构成重写。
class Person
{
public:
//一般把析构函数定义成虚函数,产生多态行为,保证资源被正常释放
virtual ~Person()
{
cout << "~Person" << endl;
}
};
class Student : public Person
{
public:
virtual ~Student()
{
if (_name)
{
delete[] _name;
cout << "delete[] _name" << endl;
}
cout << "~Student" << endl;
}
private:
char* _name = new char[100];
};
void test()
{
Person* ps = new Student;
delete ps;
Person* ps2 = new Person;
delete ps2;
}
4.4 override和final
- override :检查函数是否重写了父类的某一个虚函数,强制重写;
- final:修饰虚函数,表示该虚函数不能再被重写
class Person
{
public:
//虚函数
virtual void buyTicket()
{
cout << "全价" << endl;
}
//final修饰虚函数,表示该虚函数不能被子类重写
virtual void fun()final
{}
};
class Student : public Person
{
public:
//override:检查函数是否重写了父类的某一个虚函数,强制重写
//override:体现接口(返回值、函数名、参数列表)继承,不继承实现,重写实现
virtual void buyTicket() override
{
cout << "半价" << endl;
}
//无法重写final函数
/*virtual void fun()
{}*/
private:
char* _name = new char[100];
};
//final修饰类,表示该类不能被继承
class A1 final
{};
class B1 : public A1 //不能将final类类型作为基类
{};
5. 重载、重写、重定义对比
5.1 重载
- 两个函数在同一个作用域
- 函数名/参数相同
5.2 重写
- 两个函数分别在基类和派生类的作用域
- 函数名/参数/返回值都必须相同(协变例外)
- 两个函数必须是虚函数
5.3 重定义
- 两个函数分别在基类和派生类的作用域
- 函数名相同
- 两个基类和派生类的同名函数不构成重写就是重定义
6. 抽象类
- 纯虚函数:在虚函数后面加上 =0;
- 抽象类:包含纯虚函数的类,也叫接口类;
- 抽象类不能实例化出对象;
- 派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象;
//抽象类:包含纯虚函数
//抽象类不能实例化
class A
{
public:
virtual void fun() = 0; //纯虚函数:函数接口=0,无函数体
};
class B :public A
{
public:
virtual void fun()
{
cout << "B::fun()" << endl;
}
};
class C :public A
{
public:
virtual void fun()
{
cout << "C::fun()" << endl;
}
};
class D:public A
{};
void test()
{
//A a1;不能实例化
//D a2;
B a3;//可以实例化,重写了虚函数
C a4;
}