什么是多态
字面意思就是多种形态,当类之间存在层次结构,通过继承来相互关联时,就会用到多态。多态意味着在调用成员时,会根据调用的函数的对象来执行不同的函数。通俗一点就是同一操作作用于不同的对象,可以产生不同的效果。
C++中的多态性具体体现在运行和编译两个方面。运行时多态是动态多态,其具体引用的对象在运行时才能确定。编译时多态是静态多态,在编译时就可以确定对象使用的形式。
静态多态
在编译期间完成的,编译器会根据实参的类型去选择调用适合的函数。典型的一种就是函数重载
动态多态
在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。虚函数
多态成立的三个条件
1、要有继承
2、要有函数重写 虚函数
3、要有父类指针(引用) 指向子类对象
多态是设计模式的基础,多态是框架的基础
1、C++中通过virtual关键字对多态进行支持
2、使用virtual声明的函数被重写后即可展现多态特性
class Cat
{
public:
virtual int Power(){return 10;}
};
class Cat1 :public Cat
{
public:
virtual int Power(){return 20;}
};
class Cat2 :public Cat
{
public:
virtual int Power(){return 30;}
};
class Mouse
{
public:
int Power(){return 25;}
};
void PlayTest(Cat &c,Mouse &m)
{
if (c.Power() < m.Power())
{
cout <<"老鼠跑了" << endl;
}
else
{
cout <<"老鼠挂了" << endl;
}
}
//main
{
Cat1 c1;
Cat2 c2;
Mouse m;
PlayTest(c1, m);
PlayTest(c2,m);
}
调用结果:
上面的例子中通过比较老鼠和猫之间的力量悬殊,来展示同一种动物的不同状态。在测试阶段传入不同对象根据条件来调用对应的函数。
那么virtual 在这里起到了一个什么作用呢?首先,之前分析过重写(存在继承的父类和子类中有相同函数名的方法),子类中定义了和父类中相同的函数(方法名相同),就是说子类重写了父类的方法。这种情况下,父类中被重写的函数依然会继承给子类,默认情况下子类中重写的函数将隐藏父类中的函数,我们可以通过类名去访问被隐藏的函数。
举个例子了解一下virtual的作用:
#include <iostream>
using namespace std;
#pragma warning(disable : 4996)
class Animal
{
public:
virtual void Call()
{
cout << "Animal: Call" << endl;
}
void Eat()
{
cout << "Animal: Eat" << endl;
}
};
class Cat:public Animal
{
public:
virtual void Call()
{
cout << "Cat: Call" << endl;
}
void Eat()
{
cout << "Cat: Eat" << endl;
}
};
void Action(Animal &al)
{
al.Call();
al.Eat();
}
void PlayObj()
{
Animal al;
Cat c;
Action(al);
Action(c);
}
int main()
{
PlayObj();
system("pause");
return 0;
}
调用结果:
这里我们是通过父类的引用来调用(父类引用可以直接引用子类对象),可以看出在没有使用virtual加以说明时,子类重写了父类的方法并使其隐藏,因此调用的时候会根据对象来对应调用。而用virtual说明后相当于重定义,传入对象后编译器会根据对象的Vptr指针,在所指的虚函数表中查找对应函数函数,并调用。可以去看多态原理探究
为什么要会用到多态
面向对象的三大概念:封装、继承、多态
封装:突破了C语言函数的概念。
继承:代码复用,使用原来写好的代码,提高效率
多态:易于扩展,可维护性高,实际开发中,提倡扩展不提倡修改。
多态相关面试题
面试题1:请谈谈你对多态的理解
多态的实现效果
多态:同样的调用语句有多种不同的表现形态;
多态实现的三个条件
有继承、有virtual重写、有父类指针(引用)指向子类对象。
多态的C++实现
virtual关键字,告诉编译器这个函数要支持多态;不是根据指针类型判断如何调用;而是要根据指针所指向的实际对象类型来判断如何调用
多态的理论基础
态联编PK静态联编。根据实际的对象类型来判断重写函数的调用。
多态的重要意义
设计模式的基础 是框架的基石。
实现多态的理论基础
函数指针做函数参数
C函数指针是C++至高无上的荣耀。C函数指针一般有两种用法(正、反)。
多态原理探究
与面试官展开讨论
面试题2:是否类的每个成员函数都声明为虚函数,为什么。
不能声明为虚函数: 普通函数(非成员函数)、静态成员函数、内联成员函数、构造函数、友元函数。析构函数可以是虚的,而且通常声明为虚函数。
普通函数(非成员函数)
只能被重载,不能被重写,在类的外部什么为虚函数也没什么意思。
静态成员函数
对于每个类来说只有一份,所有的对象都共享这个函数,不归某个对象所有,因此也没必要。
内联成员函数
其作用是在调用的位置直接展开,减少函数调用的开销。属于编译时执行,而虚函数是为了在继承后对象能够准确的执行自己的动作,是在运行时才绑定函数。
构造函数
是用来初始化类的对象的,只有在对象生成后才能发挥多态作用。如果构造函数声明为虚的,则表现为在对象还没生成的时候来定义它,则会出现编译错误。另外,构造函数不能被继承。
友元函数
它不属于类的成员函数,不能被继承。
面试题3:构造函数中调用虚函数能实现多态吗?为什么?
首先要明白虚函数的调用原理,它是通过对象的vptr指针来对虚函数表的检查和调用,那么就需要先初始化类的对象。谁在前谁在后?
面试题4:为什么要定义虚析构函数?