在介绍多态前,我们先介绍下虚函数
一、虚函数
1、定义:在类的定义中,前面有virtual关键字的成员函数就是虚函数
class example
{
virtual int func();
};
int example::func()
{}
virtual关键字只用在类定义里的函数声明中,写函数体时不用
二、多态
1、使用多态的前提:派生类的指针可以赋给基类指针
2、多态:通过基类指针调用基类和派生类同名的虚函数时(非虚函数那是覆盖用::符号确定调用)
(1)、若该指针指向一个基类对象,那么调用基类对象的虚函数
(2)、若该指针指向一个派生类对象,那么调用派生类对象的虚函数
也可通过引用调用派生类对象。
demo
#include <iostream>
using namespace std;
class Father
{
public:
virtual void func();
};
class Son1 :public Father
{
public:
virtual void func();
};
class Son2 :public Father
{
public:
virtual void func();
};
void Father::func()
{
cout << "using Father virtual function" << endl;
}
void Son1::func()
{
cout << "using Son1 virtual function" << endl;
}
void Son2::func()
{
cout << "using Son2 function" << endl;
}
int main(void)
{
Father* p;
Son1 son1;
p = &son1;
p->func();
Son2 son2;
p = &son2;
p->func();
return 0;
}
————————————————分割线—————————————————————
多态还有一种在返回值继承的使用方法
/**
* 我们将多态使用在方法的返回类型中
* Car可以是抽象类,也可以是接口,JD和Benz分别继承该类或实现该借口
*/
public class CarFactory{
public Car factory(String carName){
if(carName.equals("JD")){
return new JD();
}else if(carName.equals("Benz")){
return new Benz();
}else{
System.out.println("对比起,不伺候");
return null;
}
}
} // 剽的代码
这种就是先判断使用哪种类,然后再派生。
前面那种是根据基类指针指向的派生类对象选择执行哪个虚函数
3、用基类指针数组存放指向各类派生类的指针,就能对各个派生类对象做各种操作,这很常见
4、在非构造,非析构函数的成员函数中调用虚函数,是多态
5、在构造函数和析构函数中调用虚函数,不是多态,因为编译时即可确定调用的函数是自己的类或基类中定义的函数,不会等运行时才决定是哪个派生类
三、多态的工作原理
1、多态的关键在于通过基类指针或引用调用一个虚函数时,编译时不确定到底调用的是基类还是派生类的函数,运行时才确定——这叫动态联编。
重点:2、虚函数表:
(1)、当一个类有虚函数时,我们对他去大小,会发现多了四个字节,这多的四个字节就是虚函数表
(2)、虚函数表:每一个有虚函数的类都有一个虚函数表,该类的任何对象中都放着虚函数表的指针。虚函数表中列举这该类的虚函数地址。多出来的四字节就是用来放这个地址的。
(3)、多态的函数调用语句被编译成一系列根据基类指针所指向的对象中存放的虚函数表的地址,在虚函数表中查找虚函数地址,并调用虚函数的指令。
注:在进行变量赋值时,虚函数表是会被赋过去的
三、虚析构函数与纯虚函数,抽象类:
1、通过基类指针删除派生类对象时,通常只会调用基类的析构函数。
但删除一个派生类对象,应该先调用派生类的析构函数,在调用基类的析构函数
2、解决方法:把基类的析构函数声明为virtual,派生类的析构函数可以不进行virtual声明。
这样基类指针删除派生类对象时,会首先调用派生类析构函数,再调用基类析构函数。
3、一个类如果定义了虚函数,则应该把析构函数定义为虚函数,一个类打算作为基类使用,也应该把析构函数定义为虚函数。
注:构造函数不能为虚函数。
class Father
{
public:
virtual ~Father(){std::cout<<"using Father xigou"<<endl};
};
class Son:public Father
{
virtual ~Son(){std::cout<<"using Son xigou<<endl"};
};
4、纯虚函数:没有函数体的虚函数(没有{})
class example
{
virtual void func() = 0;
};
有纯虚函数的类叫抽象类
(1)、抽象类只能作为基类来派生新类,不能独立创建对象。
(2)、抽象类的指针和引用可以指向派生类的对象
(3)、在抽象类内部可以调用纯虚函数,但在构造函数和析构函数内部不能使用纯虚函数
(4)、如果一个类从抽象类派生而来,只有派生类实现了基类的所有纯虚函数,才成为非抽象类。
四、为什么要用多态
C++编程的两个特点:面向对象和泛型编程
封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。
代码重用,也称代码复用, 就是说你把一个功能写成一个模块, 以便再次需要相同功能的时候,可以直接使用,而不用重新开发。
作为面向对象的三大特性之一,多态也有代码重用的功能,还有解决项目中紧耦合的问题,提高程序的可扩展性·
多态可以使程序员专注以接口的调用,减少精力在核心业务内部的逻辑代码的增加
例子:
比如这个,新加类时我们就要在这里重新增加代码
用了多态后,只需要调用这个接口就可以,而不用重写内部代码
个人理解:使用多态前,两个类之间的逻辑关系是清楚地,其他类想要与这个类建立关系就要续写代码。使用多态后,一个类与其他类的关系是模糊的,任何一个其他类(基类的派生类)都可以通过接口与该类建立联系。