C++_多态

目录

知识点1【多态的概述】(了解)

1、多态的分类:静态多态、动态多态

2、虚函数的引入

3、动态多态(重要)

4、虚函数的原理(了解)

知识点2【纯虚函数】(了解)

知识点3【重载、重定义(隐藏)、重写(覆盖)】(了解)

知识点4【多态案例】(了解)

 知识点5【虚析构函数】(了解)

1、知识点的引入

 2、虚析构(解决上述问题)

3、虚析构的原理(了解)

总结:虚函数和虚析构的区别?

        虚函数:

        虚析构:

知识点6【纯虚析构函数】(了解)

虚析构:

纯虚析构:

纯虚析构 函数和非纯析构函数不同点:


知识点1【多态的概述】(了解)

1、多态的分类:静态多态、动态多态

静态多态(静态联编、编译时多态):在编译的时候 就确定了函数的入口地址。(函数重载、运算符重载)

动态多态(动态联编、运行时多态):在运行的时候 就确定了函数的入口地址。(虚函数)

2、虚函数的引入

#include <iostream>

using namespace std;
class Animal
{
public:
    void speak(void)
    {
        cout<<"动物在唱歌"<<endl;
    }
};
class Dog:public Animal
{
public:
    void speak(void)
    {
        cout<<"狗在汪汪"<<endl;
    }
};

void test01()
{
    //基类指针 指向 子类对象空间
    Animal *p = new Dog;
    p->speak();//结果是动物在唱歌
}

int main(int argc, char *argv[])
{
    test01();
    return 0;
}

//动物在唱歌

C++_多态

 

3、动态多态(重要)

        1、有继承

        2、基类指针 指向 子类空间

        3、基类的"同名"函数必须虚函数

        4、子类重写(同名、同参、同返回值类型,不同函数功能)基类的虚函数。

        这样 才能使用基类指针 操作 子类的"同名函数"

#include <iostream>

using namespace std;
class Animal
{
public:
    //虚函数
    virtual void speak(void)
    {
        cout<<"动物在唱歌"<<endl;
    }
};
class Dog:public Animal
{
public:
    //子类的同名、同参、同返回值类型的函数 默认也是virtual (子类的virtual可以省略)
    virtual void speak(void)
    {
        cout<<"狗在汪汪"<<endl;
    }
};

void test01()
{
    //基类指针 指向 子类对象空间
    Animal *p = new Dog;

    //必须要用基类指针 操作子类的同名函数
    p->speak();
}

int main(int argc, char *argv[])
{
    test01();
    return 0;
}

//狗在汪汪

C++_多态

 

#include <iostream>

using namespace std;
class Animal
{
public:
    //虚函数
    virtual void speak(void)
    {
        cout<<"动物在唱歌"<<endl;
    }
};
class Dog:public Animal
{
public:
    //子类的同名、同参、同返回值类型的函数 默认也是virtual (子类的virtual可以省略)
    virtual void speak(void)
    {
        cout<<"狗在汪汪"<<endl;
    }
};
class Cat:public Animal
{
public:
    //子类的同名、同参、同返回值类型的函数 默认也是virtual (子类的virtual可以省略)
    virtual void speak(void)
    {
        cout<<"猫在喵喵"<<endl;
    }
};
class Mouse:public Animal
{
public:
    //子类的同名、同参、同返回值类型的函数 默认也是virtual (子类的virtual可以省略)
    virtual void speak(void)
    {
        cout<<"老鼠在吱吱"<<endl;
    }
};
void animalSpeak(Animal &ob)
{
    ob.speak();
}

void test02()
{
    Dog dog;
    Cat cat;
    Mouse mouse;

    animalSpeak(dog);
    animalSpeak(cat);
    animalSpeak(mouse);
}

int main(int argc, char *argv[])
{
    test02();
    return 0;
}

//狗在汪汪
//猫在喵喵
//老鼠在吱吱

4、虚函数的原理(了解)

        1、分析Animal类

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

当拥有虚函数的基类 不涉及到继承时,会产生一个vfptr(virtual function ptr)虚函数指针,该虚函数指针指向了一张虚函数表(vftable), vftable保存基类的同名函数入口地址。

C++_多态

         2、分析Dog类:

C++_多态

 当涉及到继承时:基类的虚函数指针虚函数表 都会被继承到子类中,如果子类重写 基类的虚函数。会将虚函数表中的入口地址 更改成 子类的“同名”函数。所以这时使用基类指针调用的函数 是子类的同名函数。

C++_多态

 拓展:

C++_多态

 

知识点2【纯虚函数】(了解)

在基类中 不实现虚函数的函数体。这样的虚函数 叫纯虚函数

class Animal
{
public:
    //纯虚函数 =0 不实现函数体
    virtual void speak(void)=0;
};

如果一个类中 只要有一个纯虚函数 那么这个类 是抽象类

抽象类 不能实例化对象。

Animal ob;//err 抽象类  不能实例化对象

抽象基类 派生出子类子类必须重写基类的所有纯虚函数。否则子类 也是抽象类。

#include <iostream>

using namespace std;
class Animal
{
public:
    //纯虚函数 =0 不实现函数体
    virtual void speak(void)=0;
};
class Dog:public Animal
{
public:
    //子类的同名、同参、同返回值类型的函数 默认也是virtual (子类的virtual可以省略)
    virtual void speak(void)
    {
        cout<<"狗在汪汪"<<endl;
    }
};


void test01()
{
    //Animal ob;//err 抽象类不能实例化对象

    //基类指针 指向 子类对象空间
    Animal *p = new Dog;

    //必须要用基类指针 操作子类的同名函数
    p->speak();
}



int main(int argc, char *argv[])
{
    test01();
    return 0;
}

知识点3【重载、重定义(隐藏)、重写(覆盖)】(了解)

重载:没有继承、函数重载、运算符重载(静态多态

重定义:必须有继承,子类重定义父类的非虚函数(参数类型、个数、顺序,函数的返回值类型都可以不同)。(静态多态

重写:必须有继承,子类重写父类的虚函数(参数类型、个数、顺序,返回值类型 必须一致)(动态多态

知识点4【多态案例】(了解)

C++_多态

 

#include <iostream>

using namespace std;
//抽象制作饮品
class AbstractDrinking{
    public:
    //烧水
    virtual void Boil() = 0;
    //冲泡
    virtual void Brew() = 0;
    //倒入杯中
    virtual void PourInCup() = 0;
    //加入辅料
    virtual void PutSomething() = 0;
    //规定流程
    void MakeDrink(){
        this->Boil();
        Brew();
        PourInCup();
        PutSomething();
    }
};

class Coffee : public AbstractDrinking{
    public:
    //烧水
    virtual void Boil(){
        cout << "煮农夫山泉!" << endl;
    }
    //冲泡
    virtual void Brew(){
        cout << "冲泡咖啡!" << endl;
    }
    //倒入杯中
    virtual void PourInCup(){
        cout << "将咖啡倒入杯中!" << endl;
    }
    //加入辅料
    virtual void PutSomething(){
        cout << "加入牛奶!" << endl;
    }
};

//制作茶水
class Tea : public AbstractDrinking{
public:
    //烧水
    virtual void Boil(){
        cout << "煮自来水!" << endl;
    }
    //冲泡
    virtual void Brew(){
        cout << "冲泡茶叶!" << endl;
    }
    //倒入杯中
    virtual void PourInCup(){
        cout << "将茶水倒入杯中!" << endl;
    }
    //加入辅料
    virtual void PutSomething(){
        cout << "加入食盐!" << endl;
    }
};
//业务函数
void DoBussiness(AbstractDrinking* drink){
    drink->MakeDrink();
    delete drink;
}


void test(){
    DoBussiness(new Coffee);
    cout << "--------------" << endl;
    DoBussiness(new Tea);
}



int main(int argc, char *argv[])
{
    test();
    return 0;
}

C++_多态

 知识点5【虚析构函数】(了解)

1、知识点的引入

#include <iostream>

using namespace std;
class Animal
{
public:
    Animal()
    {
        cout<<"Animal的无参构造"<<endl;
    }

    //纯虚函数 =0 不实现函数体
    virtual void speak(void)=0;

    ~Animal()
    {
        cout<<"Animal析构函数"<<endl;
    }


};
class Dog:public Animal
{
public:
    Dog()
    {
        cout<<"Dog的无参构造"<<endl;
    }

    //子类的同名、同参、同返回值类型的函数 默认也是virtual (子类的virtual可以省略)
    virtual void speak(void)
    {
        cout<<"狗在汪汪"<<endl;
    }

    ~Dog()
    {
        cout<<"Dog的析构函数"<<endl;
    }
};


void test01()
{
    //Animal ob;//err 抽象类不能实例化对象

    //基类指针 指向 子类对象空间
    Animal *p = new Dog;

    //必须要用基类指针 操作子类的同名函数
    p->speak();

    delete p;
}



int main(int argc, char *argv[])
{
    test01();
    return 0;
}

C++_多态

 C++_多态

 2、虚析构(解决上述问题)

C++_多态

#include <iostream>

using namespace std;
class Animal
{
public:
    Animal()
    {
        cout<<"Animal的无参构造"<<endl;
    }

    //纯虚函数 =0 不实现函数体
    virtual void speak(void)=0;

    virtual ~Animal()
    {
        cout<<"Animal析构函数"<<endl;
    }


};
class Dog:public Animal
{
public:
    Dog()
    {
        cout<<"Dog的无参构造"<<endl;
    }

    //子类的同名、同参、同返回值类型的函数 默认也是virtual (子类的virtual可以省略)
    virtual void speak(void)
    {
        cout<<"狗在汪汪"<<endl;
    }

    ~Dog()
    {
        cout<<"Dog的析构函数"<<endl;
    }
};


void test01()
{
    //Animal ob;//err 抽象类不能实例化对象

    //基类指针 指向 子类对象空间
    Animal *p = new Dog;

    //必须要用基类指针 操作子类的同名函数
    p->speak();

    delete p;
}

int main(int argc, char *argv[])
{
    test01();
    return 0;
}

C++_多态

 

3、虚析构的原理(了解)

        Animal的类的布局:

C++_多态

         Dog的类的布局

C++_多态

 C++_多态

 

总结:虚函数和虚析构的区别?

        虚函数:

        虚函数一般指的是虚成员函数,成员函数名前加virtual修饰,需要实现函数体,子类必须重写 父类的虚函数(函数的参数个数、顺序、类型,返回值类型必须完全一直)。 用户可以使用父类指针 操作 子类重写的虚函数。

        虚析构:

        父类的析构函数名前加virtual修饰,需要实现函数体,子类只需实现子类的析够函数即可,用户使用delete释放 父类指针 达到释放整个子类空间的效果。

知识点6【纯虚析构函数】(了解)

虚析构:

class Base
{
    public:
        virtual ~Base()
        {
            //函数体
        }
}

纯虚析构:

class Base//抽象类
{
    public:
        virtual ~Base()=0
        {
            //必须有函数体
        }
}

纯虚析构 函数和非纯析构函数不同点:

        纯虚析构函数使得基类是抽象类,不能创建基类的对象。

上一篇:一文搞懂一致性hash的原理和实现


下一篇:【21天精听打卡 3/21】20211117 Is VR the new reality?