C++从入门到精通 (7) (多态) 连载!!!

希望文章对你们有帮助 共同进步🌹  🌹  🌹 

目录

多态

动态多态的产生条件

多态原理

静态多态和动态多态的区别

虚函数

纯虚函数(重点)

练习:饮品制作

纯虚函数和多继承(了解)

虚析构函数

虚析构应用(重点)

纯虚析构

多态整理

虚函数与纯虚函数

虚析构和纯虚析构

重载重定义重写(重要)

多态

动态多态的产生条件

1.先有继承关系

2.父类中有虚函数 子类重写父类的虚函数

3.父类的指针或引用指向子类的对象

多态原理

1.当父类写了虚函数后,类内部结构发生改变,多了一个vfptr

2.vfptr 虚函数表指针 --->vftable 虚函数表

3.虚函数表中记录着 虚函数的入口地址

4.当父类指针或引用指向子类对象,发生多态,调用是时候从虚函数中找虚函数入口地址

5.虚函数关键字 virtual

静态多态和动态多态的区别

对于父子关系的两个类 指针或者引用是可以直接转换的
静态多态是函数地址是早绑定(静态联编) 函数重载、运算符重载

class Animal
{
public:
    void speak()
    {
        cout<<"动物在说话"<<endl;
    }
};

class Cat : public Animal
{
public:
    void speak()
    {
        cout<<"小猫在说话"<<endl;
    }
};

void dospeak(Animal& animal)    //提前写好了
{
    animal.speak();
}
void test_01()
{
    Cat c;            //这是一个静态多态 地址早绑定
    dospeak(c);        //动物在说话
}    

如果以上程序想要调用小猫在说话 这个时候 函数的地址就不能早绑定(动态联编)

虚函数

动态多态在运行阶段绑定地址,地址晚绑定,动态联编

class Animal
{
public:
    //虚函数
    virtual void speak()
    {
        cout<<"动物在说话"<<endl;
    }
};

class Cat : public Animal
{
public:
    void speak()
    {
        cout<<"小猫在说话"<<endl;
    }
};

class Dog : public Animal
{
public:
    void speak()
    {
        cout<<"小狗在说话"<<endl;
    }
};

  //传参多态
  //父类指向子类空间的方式 是传参的形式实现的
void dospeak(Animal& animal)    
{
    animal.speak();
}
void test_01()
{
    Cat c;            
    dospeak(c);        //小猫在说话
    Dog d;
    dospeak(d);        //小狗在说话
}    

纯虚函数(重点)

在多态的应用中 子类调用父类重写的函数 那么父类的虚函数被覆盖了 父类必须有该虚函数

但是具体实现是浪费的 此时我们可以将父类的虚函数写成纯虚函数 只作为模板 告诉子类必须要重写虚函数

注意:父类如果有纯虚函数 该父类一定是抽象类 抽象类不能实例化 只能用于多态应用(父类指针指向子类空间)

父类的纯虚函数 子类必须全部重写 否则 子类也会变成抽象类

class Animal
{
public:
    virtual void func() = 0;    //纯虚函数格式
};

class Dog:public Animal
{
public:
    void func()
    {
        cout<<"小狗正在叫"<<endl;
    }
};

class Cat:public Animal
{
public:
    void func()
    {
        cout<<"小猫正在叫"<<endl;
    }
};

void barking(Animal *a)
{
    a->func();
}

void test_01()
{
    //Animal a;  //error  抽象类不能实例化
    barking(new Dog);   //小狗正在叫
    barking(new Cat);   //小猫正在叫
}

练习:饮品制作

制作饮品为抽象类 功能有 煮茶 冲泡 倒入 加佐料 具体实现由两个子类完成 为冲咖啡和冲茶叶

class driak
{
public:
    virtual void heat() = 0;    //煮水
    virtual void brew() = 0;    //冲泡
    virtual void pour() = 0;    //倒入
    virtual void ingredients() = 0;     //辅料
};


class tea:public driak
{
public:
    void heat()
    {
        cout<<"煮水"<<endl;
    }
    void brew()
    {
        cout<<"冲泡茶叶"<<endl;
    }
    void pour()
    {
        cout<<"倒入杯中"<<endl;
    }


    void ingredients()
    {
        cout<<"加柠檬"<<endl;
    }
};


class coffee:public driak
{
public:
    void heat()
    {
        cout<<"煮水"<<endl;
    }
    void brew()
    {
        cout<<"冲泡咖啡"<<endl;
    }
    void pour()
    {
        cout<<"倒入杯中"<<endl;
    }


    void ingredients()
    {
        cout<<"加糖和牛奶"<<endl;
    }
};


void make(driak *d)
{
    d->heat();
    d->brew();
    d->pour();
    d->ingredients();
}


void test_01()
{
    cout<<"----茶----"<<endl;
    make(new tea);
    cout<<"----咖啡----"<<endl;
    make(new coffee);
}

纯虚函数和多继承(了解)

绝大多少面向对象语言都不支持多继承 但是绝大多数面向对象的语言都支持接口概念

c++没有接口概念 但是可以通过纯虚函数来实现接口

虚析构函数

虚析构应用(重点)

在多态的应用中 如果需要释放资源时 需要使用到虚析构函数

也就是 由虚构函数的地方 就一定要写虚析构 否则会造成内存泄漏

虚函数的目的:释放子类的析构函数

class Animal
{
public:
    Animal()
    {
        cout<<"Animal构造函数"<<endl;
    }
    virtual ~Animal()
    {
        cout<<"Animal析构函数"<<endl;
    }
    virtual void func()
    {
        cout<<"动物正在叫"<<endl;
    }
};

class Dog:public Animal
{
public:
    Dog()
    {
        cout<<"Dog构造函数"<<endl;
    }
    ~Dog()
    {
        cout<<"Dog析构函数"<<endl;
    }


    void func()
    {
        cout<<"小狗正在叫"<<endl;
    }
};

class Cat:public Animal
{
public:
    Cat()
    {
        cout<<"Cat构造函数"<<endl;
    }
    ~Cat()
    {
        cout<<"Cat析构函数"<<endl;
    }

    void func()
    {
        cout<<"小猫正在叫"<<endl;
    }
};

void barking(Animal *a)
{
    a->func();
    delete a;
}

void test_01()
{
    //Animal a;  //error  抽象类不能实例化
    barking(new Dog);   //小狗正在叫
    barking(new Cat);   //小猫正在叫
}

纯虚析构

在虚析构的基础上 将虚虚构的函数去掉 具体实现类外完成

具体场景和虚析构是一样的 都是用于释放子类的析构函数

纯虚析构所在的类一定是抽象类

class Animal
{
public:
    Animal()
    {
        cout<<"Animal构造函数"<<endl;
    }
    virtual ~Animal() = 0;
    virtual void func()
    {
        cout<<"动物正在叫"<<endl;
    }
};

Animal::~Animal()   //类外实现
{
    cout<<"Animal析构函数"<<endl;
}

class Dog:public Animal
{
public:
    Dog()
    {
        cout<<"Dog构造函数"<<endl;
    }
    ~Dog()
    {
        cout<<"Dog析构函数"<<endl;
    }

    void func()
    {
        cout<<"小狗正在叫"<<endl;
    }
};

class Cat:public Animal
{
public:
    Cat()
    {
        cout<<"Cat构造函数"<<endl;
    }
    ~Cat()
    {
        cout<<"Cat析构函数"<<endl;
    }


    void func()
    {
        cout<<"小猫正在叫"<<endl;
    }
};

void barking(Animal *a)
{
    a->func();
    delete a;
}

void test_01()
{
    //Animal a;  //error  抽象类不能实例化
    barking(new Dog);   //小狗正在叫
    barking(new Cat);   //小猫正在叫
}

多态整理

虚函数与纯虚函数

虚函数和纯虚函数 都是为了使用父类指针调用子类函数

虚函数 有函数体 所在的类不是抽象类 可以实例化对象

纯虚函数 =0修饰 没有函数体 所在的类为抽象类 不可实例化对象

虚析构和纯虚析构

使用了虚函数或纯虚函数 必须使用虚析构和纯虚析构来释放空间 否则容易内存泄漏 都是为了通过父类指针来释放子类的所有空间 (子类部分、父类部分)

虚析构 有函数体 所在的类不是抽象类 可实例化对象

纯虚函数 类内没有函数体只有声明 函数体在类外实现 所在的类为抽象类 不可实例化对象

重载重定义重写(重要)

重载: 没有继承关系 函数重载或运算符重载函数名相同 函数形参的个数或类型不同 会构成重载(返回值不同不能构成重载)

重定义: 有继承关系 父类和子类的函数名相同(非虚函数) 返回值和形参可以不同 就会构成重定义

重写: 有继承关系 子类重写父类的虚函数(关键字virtual) 返回值 函数名 形参 必须和父类虚函数相同

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值