嘀嘀咕(5)
1.多态
由继承而产生的相关的不同的类,其对象对同一消息会作出不同的响应。
多态成立的条件
1 要有继承
2 要有虚函数重写
3 要有父类指针(父类引用)指向子类对象
//英雄类
class Hero
{
public:
virtual int getAd() {
return 10;
}
};
class AdvHero :public Hero
{
public:
virtual int getAd()
{
return 1001;
}
};
//怪兽类
class Monster
{
public:
int getAd() {
return 1000;
}
};
//战斗方法
void playerFight(Hero *hp, Monster *mp)
{
//多态对于编译器来讲的,也是一个动态联编,也是一个迟邦定。
if (hp->getAd() > mp->getAd()) { //hp->getAd 发生了多态
cout << "英雄胜利, 怪兽被打死" << endl;
}
else {
cout << "英雄挂了,怪兽赢了" << endl;
}
}
Hero h;
Monster m;
playerFight(&h, &m);
AdvHero advH;
playerFight(&advH, &m);
原理
当类中声明虚函数时,编译器会在类中生成一个虚函数表;
当存在虚函数时,每个对象中都有一个指向虚函数表的指针(vptr指针)
class Parent
{
public:
Parent(int a) {
this->a = a;
}
virtual void func(int a)
{
cout << "Parent::func(int)..." << endl;
}
void func(int a, int b, int c) //不是虚函数
{
cout << "Parent::func(int ,int ,int )...." << endl;
}
private:
int a;
};
class Child :public Parent
{
public:
Child(int a, int b) :Parent(a)
{
this->b = b;
}
virtual void func(int a)
{
cout << "Child: func(int)..." << endl;
}
void func(int a, int b) {
cout << "Child :func(int ,int )..." << endl;
}
virtual void func(int a, int b, int c)
{
cout << "Child ::func(int ,int ,int )..." << endl;
}
private:
int b;
};
void myFunc(Parent *pp)
{
pp->func(10); //结果图1
//pp->func(10,20,30); //结果图2
}
Parent pp(10);
Child pc(100,200);
myFunc(&pp); //Parent *pp = &pp; pp->func(10); 查找func是否为虚函数,若为虚函数,调用对象的vptr指针所指向的虚函数表中的函数(动态链编);若不为虚函数,直接确定被调用函数(静态链编)
myFunc(&pc);
图1:
图2:
多态用于虚析构函数
虚析构函数用于指引 delete 运算符正确析构动态对象
class A
{
public:
A()
{
p = new char[20];
strcpy(p, "obja");
printf("A()\n");
}
virtual ~A()
{
delete [] p;
printf("~A()\n");
}
private:
char *p;
};
class B : public A
{
public:
B()
{
p = new char[20];
strcpy(p, "objb");
printf("B()\n");
}
~B()
{
delete [] p;
printf("~B()\n");
}
private:
char *p;
};
class C : public B
{
public:
C()
{
p = new char[20];
strcpy(p, "objc");
printf("C()\n");
}
~C()
{
delete [] p;
printf("~C()\n");
}
private:
char *p;
};
//通过⽗父类指针把所有的⼦子类对象的析构函数都执⾏行⼀一遍
//通过⽗父类指针释放所有的⼦子类资源
void howtodelete(A *base)
{
delete base;
}
int main()
{
C *myC = new C;
//delete myC; //直接通过⼦子类对象释放资源 不需要写virtual
howtodelete(myC);//通过⽗父类的指针调⽤用释放⼦子类的资源
return 0;
}
VPTR指针分步初始化
class Parent
{
public:
Parent(int a)
{
cout << "Parent(int ..)" << endl;
this->a = a;
print();//是调用父类的print()
}
virtual void print()
{
cout << "Parent::print()...a = "<<a << endl;
}
private:
int a;
};
class Child :public Parent
{
public:
Child(int a, int b) :Parent(a) //在调用父类的构造器的时候,会将vptr指针当做父类来处理。
//此时会临时指向父类的虚函数表
{
cout << "Child (int ,int )" << endl;
this->b = b;
print();//此时vptr指针已经回到了 子类的表, 调用的是子类的print函数。
}
virtual void print() {
cout << "Child ::Print()..b = " << b << endl;
}
private:
int b;
};
c模拟多态:函数指针做函数参数
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a-b;
}
int libfun(int (*pDis)(int a, int b), int a, int b)
{
//add(1,3); //直接调⽤用add函数
printf("%d", pDis(a, b)); //通过函数指针做函数参数,间接调⽤用add函数
return 0;
}
int main(void)
{
int (*pfun)(int a, int b);//定义⼀一个函数指针pfun 指向 int ()(int, int)函数类型
pfun = add;
//pfun = sub;
libfun(pfun,3,4);
return 0;
}
2.重载、重写、重定义
重载:发生在同一个作用域下
重写:父子类中,虚函数被子类重写
重定义:
a 如果派生类的函数和基类的函数同名,但是参数不同,此时,不管有无
virtual,基类的函数被隐藏。
b 如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没
有vitual关键字,此时,基类的函数被隐藏。