多态
实现程序的统一性,多态分为编译时多态(
重载
),运行时多态(覆盖
)。
实现手段:(父类的指针、引用和virtual)
1、父类的指针,指向子类
2、父类的对象,等于子类的对象
3、父类的引用,等于子类的对象
#include<iostream>
using namespace std;
class Base
{
public:
void show()
{ cout<<" This is Base show()"<<endl;}
};
class Child:public Base
{
public:
void show()
{cout<<" This is Child show()"<<endl;}
};
void main()
{
Child c;
Base *bp=&c;
Base &b=c;
Base ba=c;
bp.show();
b.show();
ba.show();
}
因此,若要实现多态性,应该通过覆盖和重载实现。利用虚函数实现覆盖。
#include<iostream>
using namespace std;
class Base
{
public:
virtual void show()
{ cout<<" This is Base show()"<<endl;}
};
class Child:public Base
{
public:
virtual void show()
{cout<<" This is Child show()"<<endl;}
};
void main()
{
Child c;
//实现多态的三种方式
Base *bp=&c;
//Base &b=c;
//Base ba=c;
//This is Child show()
bp.show();
}
1、虚函数的声明和有缘函数一样,声明时,加上关键字,在类外实现时,不需要关键字;在类里面实现时,需要加上关键字。
2、实现多态后,基类一旦声明为虚函数,子类的同名同参同返回值类型,也一定虚函数,子类中的virtual有无不影响。
虚函数
一个函数的前面加上关键字virtual,会构成虚函数。
#include<iostream>
using namespace std;
class Base
{
public:
Base()
{}
~Base()
{}
public:
virtual void Eat()
{ cout<<"This is Base Eat()"<<endl;}
virtual void Sleep()
{cout<<"This is Base Sleep()"<<endl;}
private:
int m_x;
};
void main()
{
Base b;
}
虚表保存了虚函数的地址
#include<iostream>
using namespace std;
class Base
{
public:
Base():m_y(0)
{}
~Base()
{}
public:
virtual void Eat()
{ cout<<"This is Base Eat()"<<endl;}
virtual void Sleep()
{cout<<"This is Base Sleep()"<<endl;}
private:
int m_y;
};
class Child:public Base
{
public:
Child():m_x(0)
{}
~Child()
{}
public:
void Eat()
{cout<<"This is Chid Eat()"<<endl;}
void Sleep()
{cout<<"This is Child Sleep()"<<endl;}
private:
int m_x;
};
void main()
{
//构造子类对象,先调用基类对象,基类中有虚函数,因此会有__vfptr,有虚表,保存了所有基类虚函数的地址
Child p;
Base *pa=&p;
//This is Chid Eat() 原因如下
pa->Eat();
}
过程:
虚表的情况:
1、子类虚函数函数覆盖了基类虚函数时,子类和基类各有一个虚表信息,并且最 终子类的虚函数会覆盖基类的虚函数
2、子类虚函数,没有覆盖基类的虚函数,会把子类的虚函数保存在基类虚表后面
3、子类多继承基类 没有覆盖时,会把子类的虚函数保存到第一个基类的虚表中
4、子类多继承基类时,子类覆盖了基类的虚函数,会把子类在内阁虚表中都保存一份
纯虚函数
概念:
纯虚函数:基类不具体实现其成员函数(这种类也叫
抽象类)
,主要用于派生子类,不能实例对象,可以定义指针
格式:virtual 返回值类型 函数名 参数列表 = 0;
声明:
当一个具体类继承纯虚函数时,在该类中必须完全实现基类中所有的纯虚函数,该类才可以实例对象。
#include<iostream>
using namespace std;
class Animal
{
//抽象类
public:
Animal()
{}
~Animal()
{}
public:
//纯虚函数
virtual void Eat()=0;
virtual void Sleep()=0;
};
class Person:public Animal
{
public:
Person()
{}
~Person()
{}
public:
virtual void Eat()
{ cout<<"This is Person Eat()."<<endl;}
//若不是全部实现基类的纯虚函数,则子类对象pp不能构造
//virtual void Sleep()
//{ cout<<"This is Person Sleep()"<<endl;}
};
int main()
{
//子类的对象
Person pp;
}
抽象类的作用,为子类提供公共接口
#include<iostream>
using namespace std;
class Animal
{
public:
Animal()
{}
~Animal()
{}
public:
virtual void Eat()=0;
virtual void Sleep()=0;
};
class Person:public Animal
{
public:
Person()
{}
~Person()
{}
public:
virtual void Eat()
{ cout<<"This is Person Eat()."<<endl;}
virtual void Sleep()
{ cout<<"This is Person Sleep()"<<endl;}
};
class Dog:public Animal
{
public:
virtual void Eat()
{ cout<<"This is Dog Eat()."<<endl;}
virtual void Sleep()
{ cout<<"This is Dog Sleep()"<<endl;}
};
//用为子类提供公共接口来实现不同功能,基类可以定义指针
void fun(Animal *aa)
{
aa->Eat();
aa->Sleep();
}
int main()
{
Person pp;
Dog dog;
fun(&pp);
fun(&dog);
}
总结:
1、虚函数中移动有虚表,且用一个虚指针指向虚表
2、满足覆盖的条件:父类为虚函数,子类也为虚函数,用子类的虚函数去覆盖了基类的函数
3、纯虚方法,是为了派生子类,为子类提供公共的接口
4、多态的两种表现,通过基类的指针、和引用方式来实现多态