类的多态

来源:互联网 发布:江南大学网络教育好吗? 编辑:程序博客网 时间:2024/05/18 01:35

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");

}

0 0
原创粉丝点击