1.多态的概念
多态的前提的是继承。当不同的对象去完成同一种行为时会产生不同的结果就是多态的通俗意义。
例如学生、成人两个对象去完成买票这个行为,那么学生的结果是获得半价,而成人获得的结果的是全价。
2.多态的定义及实现
2.1构成多态的两个硬性条件
调用的函数必须是虚函数,且派生类必须对虚函数重写;必须是基类的引用或指针调用虚函数。
虚函数就是在成员函数之前加一virtual关键字。虚函数的目的就是为了实现多态。
下面的程序是一个多态的案例:
class Person
{
public:
virtual void print()
{
cout << "Person::全价" << endl;
}
};
class Student : public Person
{
public:
virtual void print()
{
cout << "Student::半价" << endl;
}
};
void test(Person* ptr) //传入的对象不同调用不同的虚函数
{
ptr->print(); //必须是基类的指针或引用调用虚函数
}
int main()
{
Student s;
Person p;
test(&p);
test(&s);
return 0;
}
2.2虚函数的重写
派生类中有一个与基类完全相同的虚函数(返回值、函数名、参数),但是定义是派生类想要的,这样的,称派生类完成了对基类虚函数的重写(覆盖)。
派生类的重写虚函数不一定非要加virtual关键字,因为基类被继承之后其虚函数依旧保持其原有的虚函数属性,但是这样的代码风格是不规范的。
3.抽象类
在虚函数的后面写上 =0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口
类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生
类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。
class Car
{
public:
virtual void Drive() = 0;
};
class Benz :public Car
{
public:
};
int main()
{
Benz b; //错误,Benz类没有完成基类纯虚函数的重写
return 0;
}
3.1接口继承和实现继承
普通成员函数的继承是一种实现继承,派生类继承了基类普通成员函数的所有东西(返回值、函数名、参数列表、函数定义)。虚函数的继承是一种接口继承,派生类只继承了基类虚函数的接口(返回值、函数名、参数列表),其目的就是为了派生类能够对虚函数进行重写(即使没有对基类虚函数进行重写,派生类依然保持基类原有虚函数,这是特性)。
下面是一道练习题:
// 下面程序输出结果是什么?
class A
{
public:
virtual void func(int val = 1) { std::cout << "A->" << val << std::endl; }
virtual void test() { func(); }
};
class B : public A
{
public:
void func(int val = 0) { std::cout << "B->" << val << std::endl; }
};
int main(int argc, char* argv[])
{
B* p = new B;
p->test();
return 0;
}
// A: A->0 B : B->1 C : A->1 D : B->0 E : 编译出错 F : 以上都不正确
解析:使用派生类指针p指向派生类对象,那么这是一次普通函数调用。调用的函数是基类继承到派生类的虚函数,但是派生类没有对此函数完成重写,所以调用func函数时的this指针是基类指针,是一次多态调用。这个基类指针指向的是派生类对象(外部new了一个派生类对象),所以调用的是派生类重写之后的func函数,又因为接口继承,所以派生类的func函数的参数的缺省值为1,所以最终结果为B->1。