嘀嘀咕(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关键字,此时,基类的函数被隐藏。
posted @ 2018-11-07 20:20  神秘的火柴人  阅读(194)  评论(0编辑  收藏  举报