C++笔记(六)

三。多态
1.多态概念
    (1)在基类指针指向派生类的情况下,根据外部传入的对象类型的不同,调用不同派生类自己的成员函数;
        为了实现上述的功能,需要做一些额外的操作;
        将基类的函数设为虚函数:在函数声明前加一个virtual关键字。
        class Animal
        {
         public:
             void eat()
             ...
             virtual void sleep()
             ...
         }


         class Cat: public Animal
        {
         public:
             void eat()
             ...
             void sleep()
             ...
         }
         class Fish:public Animal
        {
         public:
             void eat()
             ...
             void sleep()
             ...
         }
         pa是Animal*类型的指针,所以只能调用Animal*的内部函数。
         void func(Animal * pa)
         {
            1.pa是Animal*类型的指针
            2.eat是一个普通成员函数
            3.直接调用Animal::eat
             void eat()
             ...
            1.pa是Animal*类型的指针
            2.sleep是一个虚函数,所以不能直接使用Animal的sleep函数
            3.根据某些规则,去寻找真正需要执行的函数;如果是一个Animal对象,就执行Animal::sleep;如果是一个Cat的对象,就执行Cat::sellp。
             void sleep()
             ...
         }
         根据传入的对象的判断具体需要执行的函数。


         Animal *pa = new Animal;
         Cat *pc = new Cat;
         Fish *pf = new Fish;
     


    (2) virtual关键字修饰的函数叫做虚函数。
          多态:同样一条调用语句,可以有多种不同的形态。
          函数重写:sleep在基类中是一个虚函数,在派生类中被重定义。
          静态联编/早起联编:在编译期间就可以确定具体执行哪个函数,怎么调用怎么执行的。
          动态联编/迟绑定:在编译期间无法确定具体调用哪个函数,在运行的时候才能确定具体调用哪一个函数。


    (3) 多态的使用
             1.发生在基类和派生类之间
             2.必须要有虚函数
             3.必须要有基类指针指向派生类对象


2.析构与多态
    (1)~A(){}
          
         void func(A *pa)
         {
             delete pa;
         }
A *a1 = new A;
         B *b1 = new B;
         func(a1);


        通过基类的指针释放派生类的对象。
        1.pa是A的指针
        2.delete调用析构函数,A的析构函数是一个普通函数
        3.调用A的析构函数
        4.沿着A的继承路径向上找它的父类,A没有父类
    (2)virtual ~A(){}


         void func(A *pa)
         {
        delete pa;
          }
          A *a1 = new A;
          B *b1 = new B;
          func(b1);
     第一次调用B的析构:将基类的析构函数设为虚函数(可以防止内存泄露)
        
         1.pa是A的指针
         2.delete调用析构函数,A的析构是一个虚函数
         3.通过某种方式,找到对象的析构函数进行调用
         4.沿着对象的继承路径向上找它的父类


3.多态的实现
       如何去确定具体调用的是哪一个类呢?
            引用了一个虚函数表指针。
     (1)class Animal
        {
         public:
             void eat()
             ...
             virtual void sleep()//有virtual时sizeof Animal为8
             ...               //没有virtual时,sizeof Animal为4
             virtual void func1()
             ...        
             virtual void func2()
             ... 
             virtual void func3()
             ...
         private:
             int a;
         }


         class Cat: public Animal
        {
         public:
             void eat()
             ...
             void sleep()
             ...
         private:
             int b;
         }
         class Fish:public Animal
        {
         public:
             void eat()
             ...
             void sleep()
             ...
        private:
             int c;
         }


     (2)虚函数表指针:放在对象的开始位置
          C *pc = new C; 
         func(pc);
          
        A           A的虚函数表:类的虚函数地址
 eat()时:            
 A *pa ---> vfptr ----> Animal::sleep()
                      a             Animal::func1()
                                     Animal::func2()
                                     Animal::func3()


    B           B的虚函数表指针
sleep()时: 
 A *pa ---> vfptr --->  Cat::sleep()
                     a           ...
                    b           ...


4.虚函数与虚继承
        class A
        {
        public:
           virtual func1()
           {
           }
        public:
           int a;
        };
        
        class B
        {
        public:
           virtual func2()
           {
           }
        public:
           int b;           
        };               
                      
        A虚函数表                                      B虚函数表
                                 vfptr    ----->       func2()
                                               ->(-4)
                                 vbptr --->    
                                              ->(8)
                                    b
      func1() <----      vfptr 
                                   a




5.多态使用
    (1)在构造函数中是无法使用多态的。
         class A
        {
         public:
             A()
             {
                 print();
                 printf("A 的构造函数");
             }
             void print()
             {
                
                 printf("A 的打印函数");
             }
         private:
             int a;
         }     


         class B
        {
         public:
             B()
             {
                 print();
                 printf("B 的构造函数");
             }
             void print()
             {
                
                 printf("B 的打印函数");
             }
         private:
             int b;
         }
        
         B b1;//1.A的构造函数
              //2.A的打印函数
              //3.B的构造函数
              //4.B的打印函数
         无法做到选择,因为B的vfpt在不同的函数块中指向不同的地方。
     (2)不要用基类指针去操作派生类的数组,基类指针和派生类指针有可能步长不一致。
         B *pb = arrB;//pb[i] = *(pb+i)
                             //pb+i = (int)pb+sizeof(B)*i = (int)pb+12*i
         A *pa = pb;  //pa[i] = *(pa+i)
                            //pa+i = (int)pa+sizeof(A)*i = (int)pa+8*i


6.纯虚函数和抽象类
      纯虚函数:虚函数只有函数声明,没有 函数实现,将函数体改为 =0。
                virtual double Gets() = 0;
      包含纯虚函数的类叫做抽象类:
                1.抽象类不能实例化对象;
                2.一个类如果继承自抽象类,则必须实现抽象类的所有纯虚函数;假如不实现,则该类将变为一个抽象类;
                3.抽象类可以定义对象指针,用来操作派生类。


7.接口(抽象类:只有函数,没有参数)
         多重继承接口是不会产生二义性的。


#include <iostream>


class A
{
public:
virtual void add() = 0;
virtual void print() = 0;
};


class B
{
public:
virtual void mul() = 0;
virtual void print() = 0;
};


class C:public A,public B
{
public:
C(int a,int b)
{
this->a = a;
this->b = b;
}
void add()
{
ret = a+b;
}


void mul()
{
ret = a*b;
}


void print()
{
printf("ret = %d\n",ret);
}
private:
int a;
int b;
int ret;
};


void add(A *pa)
{
pa->add();
pa->print();
}


void mul(A *pa)
{
        pa->mul();
        pa->print();
}


int main()
{
/*C c(2,5);
c.add();
c.print();
c.mul();
c.print();*/


C *pc = new C(3,4);
add(pc);
mul(pc);


return 0;
}        
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值