1.多态的意义:C++程序中允许大量的同名函数出现,体现出多态的使用价值。
2.静态多态:静态多态是指通过“彼此单独定义但支持共同操作的具体类”来表达共同性,也就是说必须存在同名的成员函数。编译时,系统自动调用相应的函数。也称编译时的多态。
3.动态多态:在编译阶段,系统是无法判断此次调用应执行哪一段函数代码。只有到了运行过程中执行到此处时,才能临时判断应执行哪一段函数代码,这种处理方式称为动态多态。
4.动态多态主要通过继承机制和虚函数来实现。
5.虚函数:在定义某一基类时,若将其中的某一非静态成员函数的属性声明为virtual,则称该函数为虚函数。virtual 返回类型 函数名 ( 参数表 ){…… };
6.定义虚函数时需要注意:
1)虚函数是非静态的、非内联的成员函数,而不能是友元函数,但虚函数可以在另一个类中被声明为友元函数。
2)虚函数声明只能出现在类定义的函数原型声明中,而不能在成员函数的函数体实现的时候声明。
3)一个虚函数无论被公有继承多少次,它仍然保持其虚函数的特性。
4)若类中一个成员函数被说明为虚函数,则该成员函数在派生类中可能有不同的实现。
5)定义了虚函数后,程序中声明的指向基类的指针就可以指向其派生类。
7.多态性带来的好处,可以通过基类的引用或指针来指明一个对象(包含其派生类的对象),当调用函数时可以自动判断调用的是哪个对象的函数。
8.一个函数如果被声明为虚函数,表明该函数在继承的类中可能被重载,当调用该函数时应当查看以确定调用哪个对象对应的函数。
9.使用引用的方法也能实现父类对象调用子类的函数。
10.当查看具体是调用谁的函数时,应该去寻找本质上new的对象是谁。
11.当程序中出现虚函数时,会自动的生成一个虚函数表,在字节上会多出4个字节,指针指向虚函数表。
12.虚函数表是一个函数指针表,每一个表项都指向一个函数。
13.虚函数表只包含虚函数的指针,没有函数体。实现上是一个函数指针的数组。
14.如果派生类重载(override)了该项对应的虚函数,则派生类虚函数表的该项指向重载后的虚函数,没有重载的话,则沿用基类的值。
15.由于使用了虚函数的类内会多虚表指针,所以该类的数据大小会多一个指针大小4。
16.类中的成员函数都有公用的函数代码段,不占用类内的内存,只有数据成员才会占内存。
17.代码如下:
#include "Utility.h"
/*
多态:
1、静态多态:
1) 同样的数据,以不同的形式解析,得到的结果也不一样
2) 函数重载
3) 运算符重载
4)模板
2、动态多态:以类的继承和虚函数(虚函数表)来实现
特点:
1、基类中要有虚函数
2、子类可以选择覆盖/不覆盖基类中的虚函数
3、使用时:基类的指针指向子类的对象
*/
class CMonster
{
public:
virtual void Attack(){cout<<"攻击..."<<endl;}
};
class CJinMonster : public CMonster
{
public:
virtual void Attack(){cout<<"近身攻击..."<<endl;}
};
class CYuanMonster : public CMonster
{
public:
virtual void Attack(){cout<<"远程攻击..."<<endl;}
};
void main()
{
CMonster* monsterArray[10] = {};
for( int i = 0 ; i < 10 ; ++i )
{
CMonster * pTemp = NULL;
if( i%2 == 0 )
pTemp = new CJinMonster();
else
pTemp = new CYuanMonster();
monsterArray[i] = pTemp;
}
for ( int i = 0 ; i < 10 ; ++i )
{
monsterArray[i]->Attack();
}
system("pause");
}
//#include "Utility.h"
定义一个"函数指针"数据类型
//typedef void(*PFunc)(void);
//class CA
//{
//public:
//virtual void Func1(){cout<<"CA类中Func1被调用"<<endl;}
//virtual void Func2(){cout<<"CA类中Func2被调用"<<endl;}
//};
//class CB :public CA
//{
//public:
//virtual void Func1(){cout<<"CB类中Func1被调用"<<endl;}
//};
思考:类的sizeof操作取决于什么?
//class CTemp
//{
//public:
//int m_iValue;
//public:
//int getValue()const{return m_iValue;}
//void setValue(int _iValue){ m_iValue = _iValue;}
//virtual void test(){}
//};
//void main()
//{
//PFunc pFunc = NULL;
//CA object;
//cout<<"基类CA的虚函数表的地址:"<<(int*)&object<<endl;
//cout<<"基类CA的虚函数表的第一元素的地址:"<<
//(int*)*(int*)&object<<endl;
//for( int i = 0 ; i < 2 ; ++i )
//{
//pFunc = (PFunc)*(((int*)*(int*)&object)+i);
//pFunc();
//}
////CTemp temp;
////cout<<sizeof(temp)<<endl;
//system("pause");
//}
18. 纯虚函数:virtual 返回类型 函数名 ( 参数表 )= 0;
19.纯虚函数的作用是为派生类提供一个一致的接口。
20.带有纯虚函数的类称为抽象类。
21.抽象类只能作为基类使用,其纯虚函数的实现由派生类给出,但派生类仍可不给出纯虚函数的实现,继续作为抽象类存在。
22.抽象类不能定义对象,一般将该类的构造函数说明为保护的访问控制权限。
23.可以声明一个抽象类的指针和引用。通过指针和引用,可以指向并访问派生类对象,进而访问派生类的成员,这种访问是具有多态特征的。
24.如果基类的指针直接指向派生类,则它只能指向派生类中基类的数据。
25.纯虚函数指针在调用子类的方法时,调用哪个方法,关键看提供给指针内存地址的对象是那个类,最终将会调用这个类中的方法。
26.使用基类指针指向派生类的对象,并且派生类对象是用new创建的,当程序使用delete运算撤销派生类对象时,这时只会调用基类的析构函数,而没有调用派生类的析构函数。(必须是new出来的)
27.虚析构函数是为了解决这样的问题:基类的指针指向派生类对象,并用基类的指针删除派生类对象。
28.抽象类是准备被用做基类的,基类必须要有一个虚析构函数,纯虚函数会产生抽象类,所以方法很简单:在想要成为抽象类的类里声明一个纯虚析构函数
29.如果想使一个类成为抽象类,但刚好又没有任何纯虚函数,可以将析构函数变为纯虚析构函数。
30.纯虚析构函数和纯虚函数不太一样,必须由抽象类自身实现。
31.代码如下:
#include "Utility.h"
/*
纯虚函数:一个类中的函数是虚函数,并且该虚函数 = 0 ;
格式:virtual 返回值类型 函数名(参数表) = 0 ;
特点:如果有一个类继承了带有纯虚函数的类,那么在子类中
必须重写该纯虚函数(填写函数体)。
能够避免我们忘记
抽象类:带有纯虚函数的类就被称之为抽象类
特点:1、带有纯虚函数
2、抽象类不能实例化对象
3、天生用来做为基类,去派生子类
接口:具有纯虚函数的类 (Java:Runable,Run extends ,implaments )
*/
class CMonster
{
public:
CMonster(){cout<<"基类构造函数"<<endl;}
//虚析构函数:为了解决基类指针指向子类对象,当
//我们删除该指针的时候,只会调用基类的析构函数,
//而不会调用子类的析构函数。(这种情况有可能会
//造成子类中的内存泄漏)
virtual ~CMonster(){cout<<"基类析构函数"<<endl;}
virtual void Attack() = 0;
virtual void Defense(){};
void ShowMe(){};
};
//当一个类继承了抽象类,如果没有覆盖基类中的纯虚函数,
//那么该类依然是一个抽象类
class CJinMonster : public CMonster
{
public:
CJinMonster(){cout<<"子类构造函数"<<endl;}
~CJinMonster(){cout<<"子类析构函数"<<endl;}
virtual void Attack(){cout<<"近战攻击..."<<endl;}
};
void main()
{
CMonster monster;//反例:抽象类不能实例化对象
//CJinMonster monster_Jin;//子类中重写了基类中的纯虚函数,
////子类不是抽象类,可以实例化
CMonster * pMonster = new CJinMonster();
pMonster->Attack();
if(pMonster)
{
delete pMonster;
pMonster = NULL;
}
system("pause");
}
/*
纯虚析构函数,它与纯虚函数的区别:
对于纯虚析构函数,我们需要在基类的cpp文件中实现该纯虚析构函数
对于纯虚函数,我们需要在子类中覆盖基类中的纯虚函数
*/
--------多态的应用1--------------
//#include "Utility.h"
//
//class CVehicle
//{
//public:
//virtual void Run() const = 0;
//};
//
//class CCar:public CVehicle
//{
//public:
//virtual void Run()const{cout<<"Run a Car"<<endl;}
//};
//
//class CAirplane:public CVehicle
//{
//public:
//virtual void Run()const{cout<<"Run a CAirplane"<<endl;}
//};
//
多态的应用:基类指针作为函数参数,传递子类对象的地址
//void run_Vehicle( const CVehicle * _pVehicle)
//{
//_pVehicle->Run();
//}
//
//void main()
//{
//-------调用1:---------
////CVehicle * pVehicle = new CCar();
////pVehicle->Run();
//
////pVehicle = new CAirplane();
////pVehicle->Run();
//
////-------调用2:---------
//CCar car;
//CAirplane airplane;
//run_Vehicle(&car);
//run_Vehicle(&airplane);
//
//system("pause");
//}
//-------------多态的应用2:在游戏中的应用--------------
#include "Utility.h"
//游戏中道具模块儿的基类
class CItem
{
public:
//道具的功能/作用
virtual void itemEffect() const = 0;
};
//"剑"类
class CSword : public CItem
{
public:
virtual void itemEffect()const{cout<<"宝剑!!"<<endl;}
};
//"盾牌"类
class CShield : public CItem
{
public:
virtual void itemEffect()const{cout<<"神盾!!"<<endl;}
};
void main()
{
CItem* pItems[10] = {};
//vector<CItem*> vItems;//存储所有道具
for(int i = 0 ; i < 5 ; ++i)
{
if(i%2 == 0)
pItems[i] = new CSword();
else
pItems[i] = new CShield();
//CItem * pTemp = NULL;
//if(i%2 == 0 )
//pTemp = new CSword();
//else
//pTemp = new CShield();
//vItems.push_back(pTemp);
}
for(int i = 0 ; i < 10 ; ++i)
{
if( pItems[i])
pItems[i]->itemEffect();
}
//for (vector<CItem*>::iterator iter = vItems.begin();
// iter!=vItems.end(); ++iter)
//{
//(*iter)->itemEffect();
//}
system("pause");
}