• C++ DAY06 c++多态


    简介

    一个事物的多种形态 , 简称多态
    物的多态
    同一个人在不同人面前展现是不同的
    :
            在我面前
            在对象面前
            在朋友面前
            在父母面前
    事的多态
    吃饭
            中国人 筷子 熟食
            美国人 刀叉 7 分熟
            日本人 筷子 生食
            印度人 手
    睡觉
            中国人 床上
            日本人 榻榻米
            侧卧
            平躺
            趴着

    上行与下行

    上行

    子类转父类
    语法
            父类名 * 父类对象指针 = 子类对象指针 ;
            父类名& 父类对象名 = 子类对象 ;
    注意 :
            无风险, 无需强转

    下行

    父类转换为子类
    语法
            子类名 * 子类对象指针 = ( 子类名 *) 父类对象指针 ;
            子类名& 子类对象 = ( 子类名 &) 父类对象 ;
    注意 :
            有风险, 需强转

    重写

    重载

            同一作用域下, 函数名形同相同 ,形参列表不同        

    重定义

            函数继承时,继承关系中,函数名相同即可

    重写

            继承关系中,返回值类型相同,函数名形同,形参列表相同,函数体不同

    c++多态分类

    静态多态 ( 早绑定 , 静态联编 )
            概念:在编译阶段 就确定函数的入口地址.
            又名: 静态联编 , 早绑定
            如: 函数重载 , 运算符重载 , 重定义等
    动态多态 ( 晚绑定 , 动态联编 )
            概念: 在运行阶段确定程序入口地址
            又名: 动态联编 , 晚绑定
            如: 虚函数 , 重写

    引入

    要求 : 设计一个函数 , 根据传入的对象调用重写的方式
    代码
    1. #include
    2. using namespace std;
    3. class Anim{
    4. public:
    5. void test01()
    6. {
    7. cout << "动物test01" << endl;
    8. }
    9. };
    10. class Dog:public Anim{
    11. public:
    12. void test01()
    13. {
    14. cout << "狗类test01" << endl;
    15. }
    16. };
    17. class Cat:public Anim{
    18. public:
    19. void test01()
    20. {
    21. cout << "猫类test01" << endl;
    22. }
    23. };
    24. void method01(Anim& a)
    25. {
    26. a.test01();
    27. }
    28. int main(int argc, char *argv[])
    29. {
    30. Dog d;
    31. Cat c;
    32. method01(d);
    33. method01(c);
    34. return 0;
    35. }
    运行结果
    原因
            父类指针只能操作子类空间中的父类部分。

    虚函数

    概念

            virtual修饰的成员函数 , 就是虚函数

    语法    

    virtual 返回值类型 函数名 ( 形参列表 )
    {
            方法体
    }

    特点

    当子类转换为父类后
    使用该父类调用使用 virtual 修饰的函数 , 调用的是子类重写后的
    使用该父类调用普通函数 , 调用的是父类的该函数

    补充概念

    重写 : 在继承关系中 , 子类方法与父类方法 , 返回值类型一致 , 函数名相同 , 形参列表相同
    注意 : 子类重写父类虚函数 , 子类该函数默认为虚函数

    代码

    1. #include
    2. using namespace std;
    3. class Anim{
    4. public:
    5. virtual void test01()
    6. {
    7. cout << "动物test01" << endl;
    8. }
    9. };
    10. class Dog:public Anim{
    11. public:
    12. void test01()
    13. {
    14. cout << "狗类test01" << endl;
    15. }
    16. };
    17. class Cat:public Anim{
    18. public:
    19. void test01()
    20. {
    21. cout << "猫类test01" << endl;
    22. }
    23. };
    24. void method01(Anim& a)
    25. {
    26. a.test01();
    27. }
    28. int main(int argc, char *argv[])
    29. {
    30. Dog d;
    31. Cat c;
    32. method01(d);
    33. method01(c);
    34. return 0;
    35. }

    动态绑定条件(重要)

    有继承,子类重写父类的虚函数,父类指针或引用指向子类空间。父类指针或引用才能调用子类重写的虚函数。
    错误演示
            B b;
            //此时会调用父类的拷贝构造 , 会产生一个新的父类对象 , 该父类对象 a 与子类对象b是两个独立空间 ,所以此时使用a 对象调用 test01 依据会执行父类的 test01 函数
            //A a = b;
            A& a = b;
            a.test01();

    动态绑定原理(重要)

    父类有虚函数,产生虚函数指针指向虚函数表,表中纪录的是父类的虚函数地址。
    如果子类继承父类,那么子类会继承父类的虚函数指针以及虚函数表。
    如果子类重写父类的虚函数,会将将虚函数表纪录的入口地址修改成子类重写的函数入口地址。
    这时父类指针指向子类空间,父类指针调用虚函数就间接调用子类重写的虚函数。

    纯虚函数

    概念

            父类的虚函数没有函数体

    语法

            virtual 返回值类型 函数名(形参列表) = 0;

    示例

    1. #include
    2. using namespace std;
    3. class Anim{
    4. public:
    5. //纯虚函数
    6. virtual void test01() = 0;
    7. };
    8. class Dog:public Anim{
    9. public:
    10. void test01()
    11. {
    12. cout << "狗类test01" << endl;
    13. }
    14. };
    15. class Cat:public Anim{
    16. public:
    17. void test01()
    18. {
    19. cout << "猫类test01" << endl;
    20. }
    21. };
    22. void method01(Anim& a)
    23. {
    24. a.test01();
    25. }
    26. int main(int argc, char *argv[])
    27. {
    28. Dog d;
    29. Cat c;
    30. method01(d);
    31. method01(c);
    32. return 0;
    33. }

    注意

    纯虚函数所在的类不能直接创建对象 , 这种类被称为抽象类
    子类继承与抽象类 , 要么重写父类提供的所有纯虚函数 , 要么自己也是抽象类

    虚析函数

    引入

    1. #include
    2. using namespace std;
    3. class Anim{
    4. public:
    5. virtual void test01() = 0;
    6. Anim()
    7. {
    8. cout << "父类构造" << endl;
    9. }
    10. ~Anim()
    11. {
    12. cout << "父类析构" << endl;
    13. }
    14. };
    15. class Dog:public Anim{
    16. public:
    17. void test01()
    18. {
    19. cout << "狗类test01" << endl;
    20. }
    21. Dog()
    22. {
    23. cout << "子类Dog构造" << endl;
    24. }
    25. ~Dog()
    26. {
    27. cout << "子类Dog析构" << endl;
    28. }
    29. };
    30. int main(int argc, char *argv[])
    31. {
    32. Dog *d = new Dog();
    33. Anim *a = d;
    34. delete a;
    35. return 0;
    36. }

    结果

            问题: 没有子类析构函数调用
            解决方案: 将父类的析构函数 设置成 虚析构

    语法

    virtual ~ 析构函数 ()
    {
    }

    代码

    1. #include
    2. using namespace std;
    3. class Anim{
    4. public:
    5. virtual void test01() = 0;
    6. Anim()
    7. {
    8. cout << "父类构造" << endl;
    9. }
    10. virtual ~Anim()
    11. {
    12. cout << "父类析构" << endl;
    13. }
    14. };
    15. class Dog:public Anim{
    16. public:
    17. void test01()
    18. {
    19. cout << "狗类test01" << endl;
    20. }
    21. Dog()
    22. {
    23. cout << "子类Dog构造" << endl;
    24. }
    25. ~Dog()
    26. {
    27. cout << "子类Dog析构" << endl;
    28. }
    29. };
    30. int main(int argc, char *argv[])
    31. {
    32. Dog *d = new Dog();
    33. Anim *a = d;
    34. delete a;
    35. return 0;
    36. }

    结果:

    纯虚析构(了解)

    语法

            virtual 析构函数名 () = 0;

    注意

            需要在类外实现析构函数

    1. #include
    2. using namespace std;
    3. class Anim{
    4. public:
    5. virtual void test01() = 0;
    6. Anim()
    7. {
    8. cout << "父类构造" << endl;
    9. }
    10. virtual ~Anim() = 0;
    11. };
    12. Anim::~Anim()
    13. {
    14. cout << "父类析构" << endl;
    15. }
    16. class Dog:public Anim{
    17. public:
    18. void test01()
    19. {
    20. cout << "狗类test01" << endl;
    21. }
    22. Dog()
    23. {
    24. cout << "子类Dog构造" << endl;
    25. }
    26. ~Dog()
    27. {
    28. cout << "子类Dog析构" << endl;
    29. }
    30. };
    31. int main(int argc, char *argv[])
    32. {
    33. Dog *d = new Dog();
    34. Anim *a = d;
    35. delete a;
    36. return 0;
    37. }

    结果

    总结

    1、虚函数和纯虚函数的区别?
            虚函数和纯虚函数都是为了通过父类指针调用子类重写的虚函数。
            虚函数 不=0 修饰 , 有函数体 , 所在的类不是抽象类 , 可以实例化对象。
            纯虚函数 =0 修饰 , 没有函数体 , 所在的类是抽象类 , 不可以实例化对象。
    2、虚析构和纯虚析构的区别
            虚析构和纯虚析构都是为了通过父类指针释放子类的所有空间( 父类部分 , 子类部分 )
            虚析构不=0 修饰 , 所在的类不是抽象类 , 可以实例化对象。
            纯虚析构=0 修饰 , 类外实现函数体 , 所在的类是抽象类 , 不可以实例化对象。
    3、重载、重定义、重写的区别
            重载:没有继承,函数重载和运算符重载。函数名相同,参数的个数,顺序、类型可以不同,返回值类型不能作为重载条件。
            重定义:有继承,子类重定义父类同名函数(非虚函数)。返回值类型,形参可以相同 和 不同。
            重写:有继承,子类重写父类的虚函数。函数名,返回值类型,形参必须和父类同名函数一致。
  • 相关阅读:
    设计模式 21 状态模式
    练习 4 Web [MRCTF2020]Ez_bypass
    Flutter:定位装饰权重组件
    优思学院|为何CPK要大于1.33?
    尚硅谷html5+css3(1)
    libcurl与分片传输、断点续传相关研究
    工程效能CI/CD之流水线引擎的建设实践总结
    基础入门 - Spring Boot HelloWorld 第二节
    Python学习--with语句
    BLIP-2:冻结现有视觉模型和大语言模型的预训练模型
  • 原文地址:https://blog.csdn.net/aisheisadas/article/details/134511965