面向对象有三大特性,封装继承和多态,这次我们学面向对象的最后一个特性:多态
一、概念
通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态
二、格式
构成多态有两个条件:
- 必须通过基类的指针或者引用调用虚函数
- 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写
2.1 虚函数
被virtual修饰的类成员函数(这里的virtual和继承里是完全不同的意思)
2.2 虚函数的重写
虚函数的重写(覆盖):派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的
返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了基类的虚函数
class Person
{
public:
virtual void Print()
{
cout << "Person::全价" << endl;
}
};
class Student : public Person
{
public:
//派生类中可以不写virtual,但是不建议这么写
virtual void Print()
{
cout << "Student::半价" << endl;
}
};
class Soldier : public Person
{
public:
virtual void Print()
{
cout << "Soldier::优先" << endl;
}
};
void test(Person& p)
{
p.Print();
}
int main()
{
Soldier s;
Student stu;
Person p;
test(s);
test(stu);
test(p);
return 0;
}
2.3 重载、重写(覆盖)、隐藏(重定义)对比
2.4 析构函数
析构函数最好加上virtual,具体原因可以看如下的例子:
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;
}
如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加virtual关键字,都与基类的析构函数构成重写,虽然基类与派生类析构函数名字不同。虽然函数名不相同,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor
三、抽象类
在虚函数的后面写上 =0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。
class Car
{
public:
virtual void Drive() = 0;
virtual void Print()=0;
};
class Benz :public Car
{
public:
virtual void Drive()
{
cout << "Benz-舒适" << endl;
}
virtual void Print()
{
cout << "Benz-舒适" << endl;
}
};
class BMW :public Car
{
public:
virtual void Drive()
{
cout << "BMW-操控" << endl;
}
virtual void Print()
{
cout << "BMW-操控" << endl;
}
};
int main()
{
//错误
Car c;
//正确
Benz z;
z.Print();
return 0;
}