C++基础多态

news2024/9/22 15:50:15

目录

学习内容:

1. 多态

1.1 多态的实现

1.2 函数重写(override)

1.3 虚函数

1.4 使用多态实现的实例 

1.5 虚函数的底层实现 

1.6 重载(voerload)、重写(override)和隐藏(hide)的区别 (面试)

1.7 纯虚函数(抽象类)

1.8 虚析构函数

课外作业:

自己封装 栈和队列

队列


学习内容:

1. 多态

        面向对象的三大特征:封装、继承、多态

        多态:就是多种状态,能够实现“一物多用”,是实现泛型编程的重要途径

1.1 多态的实现

        父类的指针或引用可以指向子类的对象,进而调用子类中重写的父类的虚函数

1.2 函数重写(override)

        1> 函数重写是发生在父子类中

        2> 要求在子类中定义与父类中函数原型相同的函数

                原型相同:返回值类型、函数名、参数列表都相同

1.3 虚函数

        1> 定义格式:在定义函数前加关键字 virtual,此时的函数就是虚函数

        2> 需要在父类中定义虚函数,子类中可以进行重写也可以不重写

        3> 当一个类中,如果定义了虚函数,那么该类会增加一个指针的大小,这个指针指向虚函数表

        4> 如果一个类中的某个函数定义成虚函数,那么该类的子子孙孙类中的该函数都是虚函数,即使没有加virtual

#include <iostream>


using namespace std;


class Animal
{
public:
    string name;      //名称
public:
    Animal() {}
    Animal(string n):name(n) {}
    ~Animal() {}


    //定义虚函数
    virtual void voice()
    {
        cout<<"~~~~~~~~"<<endl;
    }
};


//定义羊类
class Sheep:public Animal
{
public:
    int leg;       //腿的个数
public:
    Sheep(){}
    Sheep(string n, int l):Animal(n), leg(l){}
    ~Sheep(){}


    //重写父类的函数
    void voice() override
    {
        cout<<"咩咩咩~~~~~"<<endl;
    }


    //定义子类自己的函数
    void show()
    {
        cout<<"我是:"<<name<<"   有"<<leg<<"条腿"<<endl;
    }


};






int main()
{
    Sheep s1("喜羊羊", 2);
    s1.voice();            //调用的是自己的
    s1.Animal::voice();      //调用父类的
    s1.show();


    //定义一个父类的引用,目标为子类
    Animal &r1 = s1;
    r1.voice();               //调用父类的
    //r1.show();

    return 0;
}

1.4 使用多态实现的实例 


    //在父类中定义虚函数
    virtual void jungle()
    {
        boss_blood -= attack;     //每攻击一次野怪就掉血
    }
};


int Hero::boss_blood = 1000;      //初始血量


//定义具体英雄类
class Assassin:public Hero
{
public:
    int speed ;      //移速加成
public:
    Assassin(){}
    Assassin(string n, int a, int s):Hero(n, a+50), speed(s){}
    ~Assassin(){}


    //重写子父类的虚函数
    void jungle()
    {
        boss_blood -= attack;     //每攻击一次野怪就掉血
    }


};


//定义具体英雄类
class Master:public Hero
{
public:
    int speed ;      //移速加成
public:
    Master(){}
    Master(string n, int a, int s):Hero(n, a+5), speed(s){}
    ~Master(){}


    //重写父类的虚函数
    void jungle()
    {
        boss_blood -= attack;     //每攻击一次野怪就掉血
    }
};


//功能函数完成打野功能
void fun(Hero &hero)
{
    hero.jungle();
    cout<<hero.name<<"攻击了暴君,目前暴君血量为:"<<hero.boss_blood<<endl;
}


int main()
{
    Assassin h1("李白", 70, 300);    //实例化刺客


    Master h2("妲己", 30, 250);         //实例化法师


    fun(h1);
    fun(h2);




    return 0;
}

1.5 虚函数的底层实现 

1.6 重载(voerload)、重写(override)和隐藏(hide)的区别 (面试)

        1> 函数重载:函数名相同,形参列表必须不同

                1、作用域相同

                2、函数名相同

                3、参数列表必须不同(个数、类型)

                4、有无 virtual 都无所谓

                5、跟返回值没有关系

        2> 函数重写:子类中重写父类的虚函数

                1、作用域发生在父子类中

                2、函数原型相同(返回值、参数个数、参数类型、函数名)

                3、父类中的函数必须要有 virtual 关键字

        3> 函数隐藏:子类中定义与父类同名的函数

                1、作用域发生在父子俩中

                2、函数名相同

                3、返回值可以不同

                4、参数相同

                5、没有virtual修饰

1.7 纯虚函数(抽象类)

        1> 对于有些类而言,类中的相关成员函数没有实现的意义,主要是让子类来完成重写操作的

        2> 以便于使用父类的指针或引用指向子类对象,调用子类中重写的父类的虚函数

        3> 我们就可以将这样的函数设置成纯虚函数

        4> 定义格式: virtual 返回值类型 函数名(形参列表) = 0;

        5> 要求子类中必须对这些纯虚函数进行重写

        6> 抽象类:包含纯虚函数的类叫做抽象类,抽象类是不能实例化对象的

        7> 如果包含纯虚函数的子类中没有重写其虚函数,那么其子类也是抽象类,子类中的该函数也还是纯虚函数

#include <iostream>


using namespace std;


class shape
{
public:
   double perimeter;
   double area;


public:
virtual void output() = 0;


};




class Circle:public shape
{
private:
    double radius;
public:
    Circle():radius(0){}
    Circle(double r):radius(r){}
    ~Circle(){}
    void output()
    {
        cout<<"周长="<<2*radius<<"pi"<<endl;
        cout<<"面积="<<radius*radius<<"pi"<<endl;
    }
};




class Rectangle:public shape
{
private:
    double width;
    double height;
public:
    Rectangle():width(0),height(0){}
    Rectangle(double w,double h):width(w),height(h){}
    ~Rectangle(){}
    void output()
    {
        cout<<"周长="<<2*(width+height)<<endl;
        cout<<"面积="<<width*height<<endl;
    }
};




int main()
{
    //shape s;                //抽象类不能实例化对象
    Circle c1(2.5);
    Rectangle r1(3.5,4.2);


    //定义父类指针
    shape *ptr = &c1;       //定义父类指针指向子类对象
    ptr->output();           //父类?子类?
    cout<<"************************"<<endl;
    //ptr->shape::output();






    return 0;
}

1.8 虚析构函数

        1> 当使用父类指针指向子类对象时,构造时会正常先构造父类后构造子类,但是在使用delete释放内存空间时,由于父类指针的作用域,只作用在子类的父类空间内,所以,只会调用父类的析构函数,子类自己的空间就泄露了

        2> 此时可以使用虚析构函数来解决:定义析构函数时,在函数头前面加关键字virtual即可

        3> 虚析构函数能正确引导delete关键字,在释放父类空间时,把子类的空间一并释放

        4> 如果父类的析构函数为虚析构函数,那么该类的子子孙孙类中的析构函数都是虚析构函数

 

#include <iostream>


using namespace std;


class shape
{
public:
   double perimeter;
   double area;




public:
shape(){cout<<"shape ::构造函数"<<endl;}
virtual ~shape(){cout<<"shape ::析构函数"<<endl;}         //定义虚析构函数
virtual void output() = 0;


};




class Circle:public shape
{
private:
    double radius;
public:
    Circle():radius(0){}
    Circle(double r):shape(),radius(r){}
    ~Circle(){}
    void output()
    {
        cout<<"周长="<<2*radius<<"pi"<<endl;
        cout<<"面积="<<radius*radius<<"pi"<<endl;
    }
};




class Rectangle:public shape
{
private:
    double width;
    double height;
public:
    Rectangle():width(0),height(0){}
    Rectangle(double w,double h):shape(),width(w),height(h){cout<<"rectangle::构造函数"<<endl;}
    ~Rectangle(){cout<<"rectangle::析构函数"<<endl;}
    void output()
    {
        cout<<"周长="<<2*(width+height)<<endl;
        cout<<"面积="<<width*height<<endl;
    }
};




int main()
{
    shape *ptr = new Rectangle(3,5);         //在堆区申请一个子类的对象,用父类的指针指向
    ptr->output();         //正常输出
    delete ptr;






    return 0;
}

课外作业:

自己封装 栈和队列

#include <iostream>
#include <string.h>

using namespace std;
using namespace std;

class Stack
{
private:
    int* elements; // 用于存储栈中的元素
    int capacity;  // 栈的最大容量
    int top;       // 栈顶指针,指向最后一个元素的位置

public:
    // 构造函数
    Stack(int size):capacity(size),top(-1) {
        elements = new int[capacity]; // 分配内存给动态数组
    }

    // 析构函数
    ~Stack(){
        delete [] elements;
    }

    // 赋值运算符重载
    Stack & operator=(const Stack &other)
    {
        if(this != &other)
        {
            int * temp = new int[other.capacity];
            for(int i = 0; i <= other.top; ++i)
                temp[i] = other.elements[i];
            delete [] elements;
            elements = temp;
            capacity = other.capacity;
            top = other.top;
        }
        return *this;
    }

    // 获取栈顶元素的引用
    int & Top()
    {
        return elements[top];
    }

    // 判断栈是否为空
    bool empty() const
    {
        return top == -1;
    }

    // 返回栈中当前元素的数量
    int getsize() const
    {
        return top + 1;
    }

    // 扩容
    void expend()
    {
        capacity *= 2; // 容量翻倍
        int * newdata = new int[capacity];
        for(int i = 0; i <= top; i++) // 复制旧数组到新数组
        {
            newdata[i] = elements[i];
        }
        delete [] elements; // 释放旧数组内存
        elements = newdata; // 指向新数组
    }

    // 入栈操作
    void push(int e)
    {
        if(top >= capacity - 1) // 如果栈已满,则扩容
        {
            expend();
        }
        elements[++top] = e; // 将元素添加到栈顶
    }

    // 出栈操作
    int pop()
    {
        if(empty()) // 如果栈为空,则输出错误信息
        {
            cout << "Stack is empty" << endl;
        }
        return elements[top--]; // 返回并移除栈顶元素
    }
};

int main()
{
    Stack s1(3);
    s1.push(2);
    s1.push(5);
    s1.push(8);
    s1.push(5);
    s1.push(3);
    s1.push(1);
    s1.push(9);
    s1.pop();
    cout<<"top element:"<<s1.Top()<<endl;
    cout<<"stack size:"<<s1.getsize()<<endl;

    Stack s2 = s1;
    cout<<"s2.top element:"<<s2.Top()<<endl;
    cout<<"s2.stack size:"<<s2.getsize()<<endl;
    return 0;
}




队列

 

#include <iostream>

using namespace std;

class queue
{
private:
    int * elements; // 用于存储栈中的元素
    int capacity;    // 栈的最大容量
    int front;    //队头
    int rear;     //队尾
public:
    //构造函数
    queue(int size):capacity(size),front(0),rear(0) {
        elements = new int[capacity];
    }
    //析构函数
    ~ queue(){
        delete [] elements;
    }
    //拷贝赋值
    queue & operator=(const queue &other);
    //访问第一个元素
    int Front();
    //访问最后一个元素
    int Back();
    //判空
    bool empty();
    //判断大小
    int getsize();
    //向队列尾部插入元素
    void push(int e);
    //删除首个元素
    void pop();
    //二倍扩容
    void expend()
    {
        capacity *= 2; // 容量翻倍
        int * newdata = new int[capacity];
        for(int i = front; i <= rear; i++) // 复制旧数组到新数组
        {
            newdata[i-front] = elements[i];
        }
        delete [] elements; // 释放旧数组内存
        front=0;
        rear = rear - front;// 重置front和rear,以适应新数组
        elements = newdata; // 指向新数组
    }


};

queue &queue::operator=(const queue &other)
{
    if(this != &other)
    {
        capacity = other.capacity;
        front = other.front;
        rear = other.rear;
        elements = new  int[capacity];
        for(int i=front;i<rear;i++)
        {
            elements[i-front] = other.elements[i];
        }
    }
    return *this;
}

int queue::Front()
{
    return elements[front];
}

int queue::Back()
{
    return elements[rear-1];
}

bool queue::empty()
{
    return rear == front;
}

int queue::getsize()
{
    return rear-front;
}

void queue::push(int e)
{
    if(rear>=capacity)   // 如果队列已满,则扩容
    {
        expend();
    }
    elements[rear] = e;  //队尾插入元素
    rear++;
}

void queue::pop()
{
    if(empty())
    {
        cout<<"queue is empty"<<endl;
    }
    else
    {
        front = (front+1) %capacity;
    }
}




int main()
{
    queue q1(3);
    q1.push(2);
    q1.push(1);
    q1.push(3);
    q1.push(7);
    q1.push(5);
    q1.push(9);
    q1.push(4);
    //q1.pop();

    queue q2=q1;
    q1.pop();
    q1.pop();
    cout<<"队列大小为:"<<q1.getsize()<<endl;
    cout<<"队头元素为:"<<q1.Front()<<endl;
    cout<<"队尾元素为:"<<q1.Back()<<endl;
    cout<<"q2队列大小为:"<<q2.getsize()<<endl;
    cout<<"q2队头元素为:"<<q2.Front()<<endl;
    cout<<"q2队尾元素为:"<<q2.Back()<<endl;
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2101598.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构,单向链表

数据结构是计算机科学中的一个核心概念&#xff0c;它研究的是数据的组织、管理和存储方式&#xff0c;以及在这些数据上进行操作时的算法。数据结构为数据的高效访问和修改提供了基础。 数组&#xff08;Array&#xff09;&#xff1a;一种线性数据结构&#xff0c;可以存储固…

kubeadm方式安装k8s

⼀、安装环境 1. 安装说明 本次以⼆进制⽅式安装⾼可⽤ k8s 1.28.0 版本&#xff0c;但在⽣产环境中&#xff0c;建议使⽤⼩版本⼤于 5 的 Kubernetes 版本&#xff0c;⽐如 1.19.5 以后。 2. 系统环境 3. ⽹络及版本环境 注&#xff1a;宿主机⽹段、Pod ⽹段、Service ⽹段…

项目管理利器:2024五款精选项目管理软件深度解析

在项目管理这个纷繁复杂的领域中摸爬滚打二十年&#xff0c;我见证了无数项目从策划到实施&#xff0c;再到成功交付的全过程。在这个过程中&#xff0c;项目管理软件如同导航灯塔&#xff0c;为团队指明了方向&#xff0c;提高了效率&#xff0c;降低了风险。今天&#xff0c;…

Spring声明式事务使用详情(知识点+案例)

目录 1、声明式事务的概念 2、Spring事务管理器 3、基于注解的Spring事务 4、Spring事务属性 4.1只读&#xff08;Read-Only&#xff09; 4.2事务超时&#xff08;Timeout&#xff09; 4.3事务异常回滚&#xff08;rollbackFor&#xff09; 4.4事务隔离级别&#xff08…

如何用Java SpringBoot打造助农捐赠平台?2025年25届毕业生必看+最新设计实现攻略!

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

【深度学习与NLP】——词嵌入Embedding技术

目录 1.词嵌入的作用 2.嵌入矩阵的计算 3.Embedding层的代码实验 词嵌入&#xff08;Embedding&#xff09;技术是一种将词汇映射到低维连续向量空间的方法。将离散的单词数据处理成连续且固定长度的向量&#xff0c;使模型可以学习和处理语义信息。 假设需要将["Are&…

终于有人把数据中台讲明白了

在大数据发展的黄金期&#xff0c;几乎所有的高科技企业都在思考一个问题&#xff1a;海量数据作为大多数企业发展不可避免的一个趋势之后&#xff0c;企业该怎么去应用这部分数据资产&#xff0c;会对其商业产生什么影响&#xff0c;如何使数据对企业产生正面的推动而不是成为…

【Oracle APEX开发小技巧 7】解决初始化数据在动态操作-变更中被识别跳出弹窗的问题

在开发时有一个场景——推送开关数据来自于初始化动态操作&#xff0c;理论上只有变更的时候才会有二次提示&#xff0c;但是因为初始化会触发变更&#xff0c;所以会有弹窗&#xff0c;这不是我们想要的结果&#xff0c;有什么办法在初次回显数据不跳出提示吗&#xff1f;​​…

day-48 分割回文串

思路 利用dfs算法&#xff0c;用ids表示当前所指向字符的位置&#xff0c;依次判断s.charAt(ids),s.charAt(ids)s.charAt(ids1)…是否为回文字符串&#xff0c;如果是则加入链表p,再递归调用dfs函数 解题过程 每次调用dfs函数后记得还原现场 Code class Solution {public St…

【STM32+HAL库】---- 基础定时器中断控制LED

硬件开发板&#xff1a;STM32G0B1RET6 软件平台&#xff1a;cubemaxkeilVScode1 新建cubemax工程 1.1 配置系统时钟RCC 1.2 配置LED LED由PA5引脚控制&#xff0c;选择PA5引脚&#xff0c;选择GPIO_Output模式 1.3 定时时间的计算 T ( 预分频系数 1 ) ( 重装载值 1 ) 时…

RedisStack十部曲之二:Redis的核心概念

文章目录 键空间修改和查询键键过期遍历键空间 客户端缓存在计算机科学中有两个难题客户端缓存的Redis实现跟踪模式的工作机制统一的键命名空间 两种连接方式缓存策略Opt-in 模式Opt-out 模式广播模式NOLOOP选项避免竟态条件当与服务器失去连接怎么办什么值得缓存 流水线请求/响…

【2024 CCF编程能力等级认证(GESP)C++ 】 计算机基础知识

目录 1. 引言2. 计算机系统结构2.1 中央处理器&#xff08;CPU - Central Processing Unit&#xff09;2.1.1 运算器 2.1.2 控制器2.1.3 性能指标2.2 存储器2.3 输入设备2.4 输出设备 3. 计算机系统层次结构4. 操作系统4.1 操作系统分类4.2 操作系统常见操作4.2.1 基本开关机操…

SpringBoot实现文件内容对比

背景 在上一篇博客中&#xff0c;我实践了WORD转换成PDF/TXT的实现方式&#xff0c;本周接到一个新的需求&#xff0c;恰好就用上了这个成果。需求如下&#xff1a;客户提供一个WORD范本给用户&#xff0c;用户范本进行修改后&#xff0c;再反馈给客户。反馈的成果多种多样&…

RocketMQ消息回溯实践与解析

文章目录 1 问题背景2 验证2.1 生产者启动2.2 消费者启动2.3 执行回溯2.4 结果验证2.5 验证小结2.5.1 分析参数2.5.2 思考 3 分析3.1 策略模式&#xff0c;解析命令行3.2 创建客户端&#xff0c;与服务端交互3.3 获取topic对应的broker地址&#xff0c;提交重置请求3.4 与 name…

TCP 拥塞控制

概念详解 TCP拥塞控制是网络通信中的一个关键机制&#xff0c;它通过动态调整发送数据的速率来避免网络拥塞。以下是TCP拥塞控制的详细概念解释&#xff1a; 拥塞窗口&#xff08;CWND, Congestion Window&#xff09;: 定义&#xff1a;发送方在收到接收方的确认&#xff08;…

华为网络工程师证书等级有哪些?怎么备考?

华为网络工程师是由华为技术厂商推出的一系列网络工程师认证&#xff0c;其主要目的就是为了培养了验证网络工程师在华为技术以及解决方案方面的拥有一定的专业知识及技能&#xff0c;该证书分为多个等级&#xff0c;涵盖了不同网络领域及技术&#xff0c;也为众多的网络工程师…

SqlServer: 安装或升级到SqlServer2022

一、下载安装包。 https://info.microsoft.com/ww-landing-sql-server-2022.html?lcidzh-CN 简单注册一下之后&#xff0c;就可以下载安装包了。 或者在我的资源中下载&#xff1a; https://download.csdn.net/download/yenange/89709660 系统要求&#xff1a; https://…

暴力破解和撞库攻击有什么区别,怎么防御暴力破解和撞库攻击

在网络世界中&#xff0c;我们的账户安全时刻面临着各种威胁。其中&#xff0c;暴力破解和撞库攻击就是常见的两种危险手段。今天&#xff0c;就让我们深入了解这两种攻击方式的含义&#xff0c;并学习如何有效地进行防护。 暴力破解的含义 暴力破解&#xff0c;就如同一个不…

java【day03】---(Vue-Element)

1 Ajax 1.1 Ajax介绍 1.1.1 Ajax概述 我们前端页面中的数据&#xff0c;如下图所示的表格中的学生信息&#xff0c;应该来自于后台&#xff0c;那么我们的后台和前端是互不影响的2个程序&#xff0c;那么我们前端应该如何从后台获取数据呢&#xff1f;因为是2个程序&#xf…

星闪NearLink短距无线连接技术

星闪NearLink短距无线连接技术&#xff0c;作为华为主导的新一代无线短距通信标准技术&#xff0c;自2020年起由中国工信部牵头制定标准&#xff0c;旨在为万物互联时代提供更高效、更稳定的连接方式。 类似技术介绍 AirDrop&#xff08;苹果&#xff09; AirDrop是苹果公司开发…