• c++(六)



    在这里插入图片描述

    多态

    概念

    多种状态(一个事物的多种状态或形态)

    <1>水在不同的温度下呈现的不同状态
    <2>买票
    学生票;
    成人票;
    老年票;
    军人;

    在c++中是如何实现多态

    绑定:就是把函数调用语句与对应的代码块进行了绑定!。

    静态多态(绑定)

    编译的时候,就知道执行的是哪一个函数体
    调用函数时,函数名是一样的,当我们传入不同参数的时候,执行的是不同的函数体(函数重载、运算符重载)

    动态多态(绑定)

    在运行的时候,才知道执行的是哪一个函数体

    案例:如果要求两个子类的周长和面积之和,就得写两个函数来实现,传入不同的对象
    在这里插入图片描述

    在这里插入图片描述

    #include 
    using namespace std;
    class Shape
    {
    public:
    	Shape(int s = 0, int c = 0) :s(s), c(c)
    	{
    		cout << "Shape构造" << endl;
    	}
    	~Shape()
    	{
    		cout << "Shape析构" << endl;
    	}
    	int getC()
    	{
    		cout << "周长:" << this->c << endl;
    		return this->c;
    	}
    	int getS()
    	{
    		cout << "面积:" << this->s << endl;
    		return this->s;
    	}
    private:
    protected:
    	int s;
    	int c;
    };
    class Rect:public Shape
    {
    public:
    	Rect(int a = 0, int b = 0) :a(a), b(b)
    	{
    		cout << "Rect构造" << endl;
    	}
    	~Rect()
    	{
    		cout << "Rect析构" << endl;
    	}
    	int getC()
    	{
    		this->c = (this->a + this->b) * 2;
    		return this->c;
    	}
    	int getS()
    	{
    		this->s = this->a * this->b;
    		return this->s;
    	}
    	void show()
    	{
    		cout << "周长:" << this->c << ",面积:" << this->s << endl;
    	}
    private:
    protected:
    	int a;
    	int b;
    };
    class Circle:public Shape
    {
    public:
    	Circle(int r = 0) :r(r)
    	{
    		cout << "Circle构造" << endl;
    	}
    	~Circle()
    	{
    		cout << "Circle析构" << endl;
    	}
    	int getC()
    	{
    		this->c = 2 * 3.14 * this->r;
    		return this->c;
    	}
    	int getS()
    	{
    		this->s = 3.14 * this->r * this->r;
    		return this->s;
    	}
    	void show()
    	{
    		cout << "周长:" << this->c << ",面积:" << this->s << endl;
    	}
    private:
    protected:
    	int r;
    };
    void Rcs(Rect & demo)
    {
    	cout << demo.getC()+ demo.getS()<< endl;
    }
    void Ccs(Circle& demo)
    {
    	cout << demo.getC() + demo.getS() << endl;
    }
    int main()
    {
    	//矩形对象
    	Rect rect(2,3);
    	Rcs(rect);
    	
    	//圆形对象
    	Circle circle(3);
    	Ccs(circle);
    
    
    }
    

    解决:用父类统一管理子类
    在这里插入图片描述
    问题:通过父类的引用操作子类的对象时,并没有执行子类的函数,执行的是子类从父类继承过来的函数
    在这里插入图片描述
    解决:目的:执行子类重定义的函数,将父类对应的函数写成虚函数
    虚函数的格式:

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

    未加victual之前,不能访问子类重定义函数的原因:
    将子类对象强制赋值给父类的引用,父类的引用所访问的范围小,访问不到子类的函数
    在这里插入图片描述

    #include 
    using namespace std;
    class Shape
    {
    public:
    	Shape(int s = 0, int c = 0) :s(s), c(c)
    	{
    		cout << "Shape构造" << endl;
    	}
    	~Shape()
    	{
    		cout << "Shape析构" << endl;
    	}
    	virtual int getC()
    	{
    		cout << "周长:" << this->c << endl;
    		return this->c;
    	}
    	virtual int getS()
    	{
    		cout << "面积:" << this->s << endl;
    		return this->s;
    	}
    private:
    protected:
    	int s;
    	int c;
    };
    class Rect:public Shape
    {
    public:
    	Rect(int a = 0, int b = 0) :a(a), b(b)
    	{
    		cout << "Rect构造" << endl;
    	}
    	~Rect()
    	{
    		cout << "Rect析构" << endl;
    	}
    	int getC()
    	{
    		this->c = (this->a + this->b) * 2;
    		return this->c;
    	}
    	int getS()
    	{
    		this->s = this->a * this->b;
    		return this->s;
    	}
    	void show()
    	{
    		cout << "周长:" << this->c << ",面积:" << this->s << endl;
    	}
    private:
    protected:
    	int a;
    	int b;
    };
    class Circle:public Shape
    {
    public:
    	Circle(int r = 0) :r(r)
    	{
    		cout << "Circle构造" << endl;
    	}
    	~Circle()
    	{
    		cout << "Circle析构" << endl;
    	}
    	int getC()
    	{
    		this->c = 2 * 3.14 * this->r;
    		return this->c;
    	}
    	int getS()
    	{
    		this->s = 3.14 * this->r * this->r;
    		return this->s;
    	}
    	void show()
    	{
    		cout << "周长:" << this->c << ",面积:" << this->s << endl;
    	}
    private:
    protected:
    	int r;
    };
    void Scs(Shape & demo)
    {
    	cout << demo.getC()+ demo.getS()<< endl;
    }
    int main()
    {
    	//矩形对象
    	Rect rect(2,3);
    	Scs(rect);
    	
    	//圆形对象
    	Circle circle(3);
    	Scs(circle);
    
    
    }
    

    在这里插入图片描述

    动态多态的实现原理

    有虚函数一定能实现动态多态吗?不一定

    多态的实现:
    <1>一个父类,有多个子类
    <2>父类中有虚函数,子类重写父类的虚函数
    <3>用父类对象的指针或者引用去操作子类的对象并调用虚函数的受,才会触发动态多态(决定性因素)

    将父类的对应的函数写成虚函数,在子类中进行重写,通过父类对象的指针/引用去操作子类对象就可以调用到子类的函数了

    指针所能访问的空间的大小:由指针指向的数据类型大小决定

    int *p; //4字节
    shape *p;// 8个字节
    

    在这里插入图片描述
    为什么添加virtual就可以访问子类的函数?

    虚函数表:保存虚函数的地址(函数指针数组)

    什么时候有虚函数表?

    一个类中一旦有了虚函数,那么这个类中就有了一个虚函数表,保存这个类中所有虚函数的地址,如果这个类被子类继承了,子类中也会有一张虚函数表

    父类的虚函数表和子类的虚函数表一样吗?

    <1>如果子类不重写父类的虚函数,那么父类和子类的虚函数表是一样的
    <2>如果子类重写了父类的虚函数,那么子类虚函数表中对应的就是子类重写之后的虚函数地址

    在这里插入图片描述

    1. 父类有虚函数,那么被子类继承之后,子类中对应的函数也是虚函数!子类中对应函数的virtual关键字可加可不加!
    2. 父类中有函数是虚函数,如果子类有重定义的话,此时被称为重写(覆盖–就是子类中的函数把从父类中继承来的给覆盖了。子类中有且只有一个这样子的函数!!!)!!!!
    3. 重写就必须保证 子类中的函数首部与父类中函数的首部是一模一样的,首部指的是:函数类型 函数名(参数列表)。

    动态内存分配中遇到的问题

    问题:把new出来的子类对象,赋值给了父对象类型的指针,在整个操作过程中,都是通过父类的指针来操作,使用delete去释放空间的时候,只执行了父类的析构函数,并没有子类的析构函数,可能会造成内存泄漏的问题(在子类的构造函数去new空间了)
    在这里插入图片描述

    在这里插入图片描述

    解决:目标:执行子类的析构函数
    将父类的析构函数写成虚函数,子类的析构自然也是虚函数!

    在这里插入图片描述

    #include 
    using namespace std;
    class Shape
    {
    public:
    	Shape(int s = 0, int c = 0) :s(s), c(c)
    	{
    		cout << "Shape构造" << endl;
    	}
    	virtual ~Shape()
    	{
    		cout << "Shape析构" << endl;
    	}
    	virtual int getC()
    	{
    		cout << "周长:" << this->c << endl;
    		return this->c;
    	}
    	virtual int getS()
    	{
    		cout << "面积:" << this->s << endl;
    		return this->s;
    	}
    private:
    protected:
    	int s;
    	int c;
    };
    class Rect :public Shape
    {
    public:
    	Rect(int a = 0, int b = 0) :a(a), b(b)
    	{
    		cout << "Rect构造" << endl;
    	}
    	~Rect()
    	{
    		cout << "Rect析构" << endl;
    	}
    	int getC()
    	{
    		this->c = (this->a + this->b) * 2;
    		return this->c;
    	}
    	int getS()
    	{
    		this->s = this->a * this->b;
    		return this->s;
    	}
    	void show()
    	{
    		cout << "周长:" << this->c << ",面积:" << this->s << endl;
    	}
    private:
    protected:
    	int a;
    	int b;
    };
    class Circle :public Shape
    {
    public:
    	Circle(int r = 0) :r(r)
    	{
    		cout << "Circle构造" << endl;
    	}
    	~Circle()
    	{
    		cout << "Circle析构" << endl;
    	}
    	int getC()
    	{
    		this->c = 2 * 3.14 * this->r;
    		return this->c;
    	}
    	int getS()
    	{
    		this->s = 3.14 * this->r * this->r;
    		return this->s;
    	}
    	void show()
    	{
    		cout << "周长:" << this->c << ",面积:" << this->s << endl;
    	}
    private:
    protected:
    	int r;
    };
    void Scs(Shape* demo)
    {
    	cout << demo->getC() + demo->getS() << endl;
    }
    int main()
    {
    
    	Shape* p = new Circle(4);
    	Scs(p);
    	delete p;
    
    
    }
    

    <1>在设计类的时候,为什么建议把析构函数写成虚函数
    防止内存泄漏
    <2>为什么不执行子类的析构函数就可能会存在内存泄漏的问题
    在子类的构造函数去new空间了

    重载、重定义、重写的区别

    重载:同一个作用域内,函数功能相似,函数名相同、参数不同,与返回值无关的一组函数
    重定义:在继承关系中,子类重定义父类的函数,函数名相同即可
    重写(覆盖):在继承关系中,子类重写父类的虚函数
    备注:函数首部必须一样
    首部:返回值类型 函数名(形式参数列表)

    抽象类

    在设计类的时候,发现这个类确实需要这样子的一个操作函数,但是在父类中不知道该怎么实现,它的所有的子类都要实现这个函数,并且所有的子类实现这个函数的效果是不一样的,这种情况下,就可以把这个函数写成纯虚函数

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

    在这里插入图片描述
    注意:抽象类是不能创建对象的
    在这里插入图片描述

    作用:就是用来被继承的,在子类中去实现这个纯虚函数(每一个子类都要去实现这个纯虚函数)
    如果子类没有实现这个纯虚函数,子类也变成了抽象类

    #include 
    using namespace std;
    class Shape //抽象类
    {
    public:
    	Shape(int s = 0, int c = 0) :s(s), c(c)
    	{
    		cout << "Shape构造" << endl;
    	}
    	virtual ~Shape()
    	{
    		cout << "Shape析构" << endl;
    	}
    	virtual int getC() = 0;//纯虚函数
    	
    	virtual int getS() = 0;
    	
    private:
    protected:
    	int s;
    	int c;
    };
    class Rect :public Shape
    {
    public:
    	Rect(int a = 0, int b = 0) :a(a), b(b)
    	{
    		cout << "Rect构造" << endl;
    	}
    	~Rect()
    	{
    		cout << "Rect析构" << endl;
    	}
    	int getC()
    	{
    		this->c = (this->a + this->b) * 2;
    		return this->c;
    	}
    	int getS()
    	{
    		this->s = this->a * this->b;
    		return this->s;
    	}
    	void show()
    	{
    		cout << "周长:" << this->c << ",面积:" << this->s << endl;
    	}
    private:
    protected:
    	int a;
    	int b;
    };
    class Circle :public Shape
    {
    public:
    	Circle(int r = 0) :r(r)
    	{
    		cout << "Circle构造" << endl;
    	}
    	~Circle()
    	{
    		cout << "Circle析构" << endl;
    	}
    	int getC()
    	{
    		this->c = 2 * 3.14 * this->r;
    		return this->c;
    	}
    	int getS()
    	{
    		this->s = 3.14 * this->r * this->r;
    		return this->s;
    	}
    	void show()
    	{
    		cout << "周长:" << this->c << ",面积:" << this->s << endl;
    	}
    private:
    protected:
    	int r;
    };
    void Scs(Shape* demo)
    {
    	cout << demo->getC() + demo->getS() << endl;
    }
    int main()
    {
    
    	Shape* p = new Circle(4);
    	Scs(p);
    	delete p;
    
    
    }
    

    在这里插入图片描述

    接口类—抽象类

    接口类其实就是抽象类的应用
    当抽象类不能实例化对象时,抽象类中的成员变量也就不能初始化,其构造函数也就不能被执行

    接口类:类中只有成员函数,没有数据成员,并且成员函数必须是纯虚函数
    作用:就是用来被继承的,描述一些能力、协议

    #include 
    using namespace std;
    //接口类
    class fly_land
    {
    public:
    	virtual void fly() = 0;//纯虚函数
    	virtual void land() = 0;
    };
    class Bird :public fly_land
    {
    public:
    	void fly()
    	{
    		cout << "bird  fly ....." << endl;
    	}
    	void land()
    	{
    		cout << "bird land....." << endl;
    	}
    };
    class Plane :public fly_land
    {
    public:
    	void fly()
    	{
    		cout << "plane fly....." << endl;
    	}
    	void land()
    	{
    		cout << "plane land....." << endl;
    	}
    };
    void dosomething(fly_land& demo)
    {
    	demo.fly();
    }
    int main()
    {
    	Bird bird;
    	dosomething(bird);
    
    	Plane plane;
    	dosomething(plane);
    }
    

    在这里插入图片描述

    空类对象的内存大小

    默认构造函数、析构函数、默认拷贝构造函数、赋值运算符函数、取值运算符函数 const 修饰的取值运算符
    空类占内存大小:1字节
    在这里插入图片描述
    在这里插入图片描述

    explicit

    作用:修饰构造函数的,对应的构造函数被称为 转换构造函数!!!!
    意义:防止构造函数单参数的时候进行自动类型的转换

    在这里插入图片描述

    final

    作用:修饰类和类中的成员函数

    修饰类

    作用:不能被继承
    在这里插入图片描述

    修饰成员函数

    作用:无法重写
    在这里插入图片描述

  • 相关阅读:
    NFT的下一个叙事:动态NFT
    BUUCTF Misc 隐藏的钥匙 & 另外一个世界 & FLAG & 神秘龙卷风
    【SCA 开源组件漏洞整改】记录遇到的问题
    告诉你,赚钱狠的人,都有一个特点?
    电影主题HTM5网页设计作业成品——爱影评在线电影(10页面)使用dreamweaver制作采用DIV+CSS进行布局
    删除的文件还能找回来吗?
    JAVAEE初阶相关内容第十六弹--网络编程
    【华为OD机试真题 python】 太阳能板最大面积【2022 Q4 | 100分】
    Modbus笔记
    SLAM从入门到精通(构建自己的slam包)
  • 原文地址:https://blog.csdn.net/xuezhe_____/article/details/139301605