• C++进阶篇-----多态


    什么是多态

    在继承关系中,不同类对象去调用同一函数,产生不同的行为。
    例如:卖火车票的时候,学生是半价,成人是全价。

    class Person {
    public:
    	virtual void BuyTicket() { cout << "买票-全价" << endl; }
    };
    class Student : public Person {
    public:
    	virtual void BuyTicket() { cout << "买票-半价" << endl; }
    
    };
    
    void Func(Person& p)
    {
    	p.BuyTicket();
    }
    
    int main()
    {
    	Person ps;
    	Student st;
    	Func(ps);
    	Func(st);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述

    多态又分为静态多态(编译时)和动态多态(运行时)
    静态多态:例如重载
    动态多态:根据具体拿到的类型确定程序的具体行为,调用具体的函数。
    多态的两个条件:
    1,必须是虚函数,派生类必须对基类进行重写
    2,必须通过基类的指针和引用去调用虚函数。 (如果是父类对象去调用方法,一定是父类的方法)

    重写的条件: 必须是虚函数,然后子类,父类的函数名,返回值,参数必须相同
    例外:1,协变 父类和子类的虚函数返回值可以是其他继承关系的父类和子类的指针或者引用
    2,析构函数,编译器将父类和子类的析构函数统一处理为destructor()。
    我们自己实现的时候最好将析构函数设置为虚函数,这样可以保证在动态生成父类子类对象后,
    能正确调用各自的析构函数
    3,子类对象的虚函数(满足构成重写)可以不加virtual关键字,编译器默认子类从父类继承的方法有虚拟属性。

    重载:在同一作用域,函数名相同,参数不同
    重写(覆盖):在继承关系中,两个虚函数分别在父类和子类,函数名,参数,返回值必须相同。
    重定义(隐藏):两个函数分别在父类和子类,同名函数。( 在继承关系中,两个同名的函数不构成重写,那一定就是重写。)

    关于关键字final (禁止该函数被重写) 和override(检查是否重写,不重写就报错)
    在这里插入图片描述

    抽象类

    在虚函数后面加上 = 0 ,该函数就是纯虚函数,有纯虚函数的类就是抽象类,抽象类不能去实例化对象。
    子类继承抽象类后,子类如果不重写该虚函数,则该子类也是抽象类。例外父类的纯虚函数可以有实体,但是没什么意义。

    class A
    {
    public:
    	virtual void Print() = 0           //这个就是纯虚函数
    	{
    		cout << "class A " << endl;
    	}
    };
    
    class B
    {
    public:
    	virtual void Print()
    	{
    		cout << "class B " << endl;
    	}
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    接口继承和实现继承

    普通函数的继承是一种实现继承派生类继承了基类函数,可以使用函数,继承的是函数的实现。虚函数的
    继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成多态,继承的是接口。所
    以如果不实现多态,不要把函数定义成虚函数

    多态的原理

    这段代码运行起来是 8个字节,为什么呢?

    class A
    {
    public:
    	virtual void Print()
    	{
    		cout << " haha" << endl;
    	}
    	
    	int _a;
    };
    
    int main()
    {
    	A a;
    	cout << sizeof(a) << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述

    class Base
    {
    public:
    	virtual void Func1()
    	{
    		cout << "Base::Func1()" << endl;
    	}
    	virtual void Func2()
    	{
    		cout << "Base::Func2()" << endl;
    	}
    	void Func3()
    	{
    		cout << "Base::Func3()" << endl;
    	}
    private:
    	int _b = 1;
    };
    
    class Derive : public Base
    {
    public:
    	virtual void Func1()
    	{
    		cout << "Derive::Func1()" << endl;
    	}
    private:
    	int _d = 2;
    };
    
    int main()
    {
    	Base b;
    	Derive d;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    在这里插入图片描述
    回到原点:多态的实现原理。当用父类的指针和引用去指向和引用子类的时候,在用父类指针去和引用去调用方法,
    调用的是子类方法。 因为子类和父类各自生成了虚表,通过虚表各自找到所需要的虚函数,从而实现了多态。

    为什么不能用父类对象呢? 因为赋值的时候,vfptr不会拷过去。

    一些概念的理解问题:
    一个类对象可以有多个虚表,因为有多继承。
    所有同类的对象公用同一张虚表,因为虚表和虚函数都是存放在代码段的(常量区)。
    虚表指针是在构造函数初始化列表阶段初始化的,而虚函数表是在编译时生成的。

    当出现多继承,也就是说子类有多个虚表,但是子类中有一个虚函数(父类中没有的),这个虚函数就
    放在第一张虚表的后面。

    构造函数初始化列表顺序只跟继承顺序有关

  • 相关阅读:
    PostgreSQL 导出数据为CSV
    吃葡萄--用图形解决算法问题(java)
    Chapter 2 Gradient Descent
    Docker和debezium是什么关系,如何部署?
    FME数据处理06:面重叠处理
    LeetCode题目笔记——6225. 差值数组不同的字符串,Python 32ms
    智慧公厕是智慧城市公共厕所形态的新一代技术支撑
    腾讯云OpenCloudOS能安装宝塔Linux面板吗?
    基于Python实现的五子棋游戏设计
    2022中国-北欧经贸合作论坛系列活动在湖北开幕 中科升哲展示“宜昌力量”
  • 原文地址:https://blog.csdn.net/CL2426/article/details/126067358