注:该课程大纲详见 https://www.cnblogs.com/inchbyinch/p/12398921.html
虚函数:
多态的机制:
注意:
//示例1:多态的两种表现形式
class CBase {
public:
virtual void SomeVirtualFunction() { cout << "This is CBase" << endl; }
};
class CDerived:public CBase {
public :
virtual void SomeVirtualFunction() { cout << "This is CDerived" << endl; }
};
//多态表现形式一:派生类指针赋给基类指针,且调用虚函数
int main() {
CDerived ODerived;
CBase * p = & ODerived;
p->SomeVirtualFunction(); //调用哪个虚函数取决于p指向哪种类型的对象
return 0;
}
//多态表现形式二:派生类对象赋给基类引用,且调用虚函数
int main() {
CDerived ODerived;
CBase & r = ODerived;
r.SomeVirtualFunction(); //调用哪个虚函数取决于r引用哪种类型的对象
return 0;
}
//示例2:诡异的多态
class Base {
public:
void fun1() { this->fun2(); } //注意此处
virtual void fun2() { cout << "Base::fun2()" << endl; }
};
class Derived:public Base {
public:
virtual void fun2() { cout << "Derived:fun2()" << endl; }
};
int main() {
Derived d;
Base * pBase = & d;
pBase->fun1();
return 0;
}
//输出结果 Derived:fun2()
//解释:在fun1()中,this是基类指针,fun2是虚函数,所以是多态
//示例3:手推运行结果
class myclass {
public:
virtual void hello(){cout<<"hello from myclass"<<endl; }
virtual void bye(){cout<<"bye from myclass"<<endl;}
};
class son:public myclass{
public:
void hello(){ cout<<"hello from son"<<endl;}
son(){ hello(); }
~son(){ bye(); }
};
class grandson:public son{
public:
void hello(){ cout<< "hello from grandson" << endl;}
void bye() { cout << "bye from grandson" << endl;}
grandson() { cout << "constructing grandson" << endl;}
~grandson(){ cout << "destructing grandson" << endl;}
};
int main(){
grandson gson;
son *pson;
pson = &gson;
pson->hello();
return 0;
}
//运行结果:
//hello from son
//constructing grandson
//hello from grandson
//destructing grandson
//bye from myclass
在游戏——魔法门之英雄无敌中,有很多种怪物,比如Dragon、Wolf、Angle、Soldier等,每种怪物都有一个类与之对应,每个怪物就是一个对象。怪物能够互相攻击,攻击敌人和被攻击时都有相应的动作,动作是通过对象的成员函数实现的。
因此常规思路可以这样:
//非多态的实现方法
class class CCreature {
protected:
int nPower ; //代表攻击力
int nLifeValue ; //代表生命值
};
//各个怪物类
class CDragon:public CCreature {
public:
void Attack(CWolf * pWolf) {
...(表现攻击动作的代码)
pWolf->Hurted( nPower);
pWolf->FightBack( this);
}
void Attack(CGhost * pGhost) {
...(表现攻击动作的代码)
pGhost->Hurted( nPower);
pGohst->FightBack( this);
}
void Hurted(int nPower) {
...(表现受伤动作的代码)
nLifeValue -= nPower;
}
void FightBack(CWolf * pWolf) {
...(表现反击动作的代码)
pWolf ->Hurted( nPower / 2);
}
void FightBack(CGhost * pGhost) {
...(表现反击动作的代码)
pGhost->Hurted( nPower / 2 );
}
};
非多态方法的缺陷:
如果采用多态的方法,则可以:
//多态的方法
class CCreature {
protected :
int m_nLifeValue, m_nPower;
public:
virtual void Attack( CCreature * pCreature) { }
virtual void Hurted( int nPower) { }
virtual void FightBack( CCreature * pCreature) { }
};
//各个怪物类
class CDragon : public CCreature {
public:
virtual void Attack(CCreature * p){
...(表现攻击动作的代码)
p->Hurted(nPower);
p->FightBack(this);
}
virtual void Hurted(int nPower) {
...(表现受伤动作的代码)
m_nLifeValue -= nPower;
}
virtual void FightBack(CCreature * p){
...(表现受伤动作的代码)
p->Hurted(m_nPower/2); //多态
}
};
具体调用过程是:
CDragon Dragon; CWolf Wolf; CGhost Ghost; CThunderBird Bird;
Dragon.Attack( & Wolf); //(1)
Dragon.Attack( & Ghost); //(2)
Dragon.Attack( & Bird); //(3)
//上面的(1),(2),(3)进入到CDragon::Attack函数后,能分别调用:
//CWolf::Hurted
//CGhost::Hurted
//CBird::Hurted
几何形体处理程序:输入若干个几何形体的参数,要求按面积排序输出。输出时要指明形状。
Input:
Output:
//程序示例
Sample Input:
3
R 3 5
C 9
T 3 4 5
Sample Output:
Triangle:6
Rectangle:15
Circle:254.34
//程序设计
#include <iostream>
#include <stdlib.h>
#include <math.h>
using namespace std;
//基类
class CShape{
public:
virtual double Area() = 0; //纯虚函数,无函数体{}
virtual void PrintInfo() = 0;
};
//派生类
class CRectangle:public CShape{
public:
int w,h;
virtual double Area(){ return w * h; }
virtual void PrintInfo(){ cout << "Rectangle:" << Area() << endl; }
};
class CCircle:public CShape {
public:
int r;
virtual double Area(){ return 3.14 * r * r; }
virtual void PrintInfo(){ cout << "Circle:" << Area() << endl; }
};
class CTriangle:public CShape {
public:
int a,b,c;
virtual double Area(){
double p = ( a + b + c) / 2.0;
return sqrt(p * ( p - a)*(p- b)*(p - c));
}
virtual void PrintInfo(){ cout << "Triangle:" << Area() << endl; }
};
CShape * pShapes[100];
int MyCompare(const void * s1, const void * s2);
int main(){
int i; int n;
CRectangle * pr; CCircle * pc; CTriangle * pt;
cin >> n;
for( i = 0;i < n;i ++ ) {
char c;
cin >> c;
switch(c) {
case 'R':
pr = new CRectangle();
cin >> pr->w >> pr->h;
pShapes[i] = pr;
break;
case 'C':
pc = new CCircle();
cin >> pc->r;
pShapes[i] = pc;
break;
case 'T':
pt = new CTriangle();
cin >> pt->a >> pt->b >> pt->c;
pShapes[i] = pt;
break;
}
}
qsort(pShapes,n,sizeof( CShape*),MyCompare);
for( i = 0;i <n;i ++)
pShapes[i]->PrintInfo();
return 0;
}
int MyCompare(const void * s1, const void * s2){
double a1,a2;
CShape * * p1 ; // s1,s2 是 void * ,不可写 “* s1”来取得s1指向的内容
CShape * * p2;
p1 = ( CShape * * ) s1; //s1,s2指向pShapes数组中的元素,数组元素的类型是CShape *
p2 = ( CShape * * ) s2; // 故 p1,p2都是指向指针的指针,类型为 CShape **
a1 = (*p1)->Area(); // * p1 的类型是 Cshape * ,是基类指针,故此句为多态
a2 = (*p2)->Area();
if( a1 < a2 )
return -1;
else if ( a2 < a1 )
return 1;
else
return 0;
}
注意:
为了实现同一个接口,不同的实现方式,C++采用了virtual函数和晚绑定技术来实现。
编译器在编译时,将类的成员函数编译成与类无关的独立函数,对象在调用该函数时,实际上依靠的是函数名、对象指针以及其他所需参数。所以,在派生类重写同名函数情况下,当用基类指针指向派生类对象时,该指针调用函数的话,就被编译成了该函数名、该基类指针、以及相关参数,这时找到的就是基类的函数,无法定位派生类的该函数。
为了解决这个问题,C++引入virtual函数。编译器在编译时,发现类中有虚函数,则自动插入一个一维虚函数表,里面存储了该类的各个虚函数地址。有虚函数的基类,其派生类自动继承了虚函数表,若派生类重写了虚函数,则其虚函数表中地址自动更新。有虚函数的类实例化对象时,编译器自动在对象内存开头加入一个虚表索引,其指向生成类的虚表。在编译时,若编译器发现调用的是虚函数,则自动采用晚绑定机制,即在运行时通过具体对象的虚表指针获取虚表,从而找到对应的函数。
指针的本质:
//示例:改变虚表指针
#include <iostream>
using namespace std;
class A {
public:
virtual void Func() { cout << "A::Func" << endl; }
};
class B:public A {
public:
virtual void Func() { cout << "B::Func" << endl; }
};
int main() {
A a;
A * pa = new B();
pa->Func();
long long * p1 = (long long * ) & a; //64位程序指针为8字节
long long * p2 = (long long * ) pa;
* p2 = * p1;
pa->Func();
return 0;
}
//运行结果:
//B::Func
//A::Func
正常情况下,删除一个派生类的对象时,应该先调用派生类的析构函数,然后调用基类的析构函数。但是通过基类的指针删除派生类对象时,通常只调用基类的析构函数,这可能会导致内存泄露。
解决办法:把基类的析构函数声明为virtual(派生类的析构函数可以不进行virtual声明)。
原文:https://www.cnblogs.com/inchbyinch/p/12398403.html