1. 什么是多态
多态(Polymorphism),从字面意思理解,就是一种事物具有多种状态,在C++里表示函数具有多种表达。多态一般出现在向上转型里边,也就是将派生类对象赋值给基类对象。我们直接看一个例子,就能明白为什么会出现多态。
#include <iostream>
using namespace std;
class People{
public:
People(char *name, int age):m_name(name),m_age(age){}
void show(){cout<<"name:"<<m_name<<", age:"<<m_age<<", no job"<<endl;} //添加virtual让Peter重新上岗
char *m_name;
int m_age;
};
class Worker:public People{
public:
Worker(char* name, int age, float salary):People(name,age),m_salary(salary){}
void show(){{cout<<"name:"<<m_name<<", age:"<<m_age<<", is a worker, salary:"<<m_salary<<endl;}}
private:
float m_salary;
};
int main(){
People *p1 = new People("Tom",24);
p1->show();
Worker p2("peter",28,5200.0);
p1 = &p2;
p1->show();
}
输出:
name:Tom, age:24, no job
name:peter, age:28, no job
在这个例子里,首先定义了一个People
类,包含姓名年龄,而且会显示为no job
(无业),然后又定义了一个Worker
,是People
的派生类,会显示is a worker
以及薪水。
在main()
函数里,我们首先将People
实例化为p1
,此时正常显示,Tom
是个无业游民,然后Worker
类的p2
赋值给p1
,发现显示Peter
也是无业游民,但实际上Peter
是一个worker
,有薪水的。
通过观察不难发现,派生类赋值给基类指针之后,基类指针指向派生类对象,可以使用其成员变量,刚才姓名和年龄能正常显示就是证明;但是却无法使用派生类的成员函数,使用的还是基类的成员函数。这是因为,指针对象依靠指向调用成员变量,依靠类别调用成员函数,在执行语句p1 = &p2
之后,p1
类别为People
类,而指向p2
(Worker类),所以就出现了使用派生类成员变量和基类成员函数的情况。
为了让Peter重新上岗,我们需要借助虚函数,在People类的show()
函数前边添加关键字virtual
,即
virtual void show(){cout<<"name:"<<m_name<<", age:"<<m_age<<", no job"<<endl;}
运行结果为:
name:Tom, age:24, no job
name:peter, age:28, is a worker, salary:5200
成功让Peter重新上岗。
2. 多态的构成条件
①首先,要有继承关系,即,发生在基类和派生类之间;
②基类和派生类要有相同函数,这个相同,指得是函数名、输入形参、输出都要一样,比重载和遮蔽要求都要高;
③在基类成员函数前面添加virtual
关键字。
3. 什么时候使用虚函数
首先,判断函数是不是定义在基类;其次,判断派生类继承的时候会不会修改这个函数,如果不会修改则不需要声明,如果会修改,则可以声明为虚函数。
这段话还是比较好理解的,多态本意就是一个函数的两种表达,如果派生类中没有修改,那么基类对象调用谁的都一样,就不存在多态了。
4. 注意事项
需要注意的有三点:
①函数头必须完全相同才达到多态的条件。如果仅是函数名一样,形参不一样,不会构成多态!
②如果定义了在基类中定义了虚函数,但是派生类中并没有能构成多态的函数,那么基类的虚函数它就是个普通成员函数。
③如果派生类的成员函数没有构成多态,基类是无论如何都访问不了的。
针对这个三个注意事项,一个例子可以说明,
#include <iostream>
using namespace std;
class A{
public:
virtual void show(){cout<<"A:show()"<<endl;}
virtual void show(int m){cout<<"A:show(int)"<<endl;}
};
class B:public A{
public:
void show(){cout<<"B:show()"<<endl;}
void show(char *n){cout<<"B:show(float)"<<endl;}
};
int main(){
A *a = new B();
a->show(); //输出 B:show()
a->show(2); //输出 A:show(int)
a->show("dddd"); //报错
}
④构造函数不会被继承,也不会构成多态。
⑤如果自定义析构函数(一般是为了收回内存),尽量将基类析构函数设置为虚函数。