当前位置: 首页 > article >正文

C++基础多态

目录

学习内容:

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.kler.cn/news/289133.html

相关文章:

  • 爬虫练习(js逆向解密)
  • 中国水资源用水紧张程度数据(栅格/0.5度)
  • win10 +git配置+学习笔记
  • windows下安装docker操作步骤
  • 【C++第十六章】多态
  • 综合评价 | 基于层次-变异系数-正态云组合法的综合评价模型(Matlab)
  • Vulkan入门系列18 - 计算着色器(Compute Shader)
  • 阳台封窗是在保温上边还是把保温拆了之后封呢?
  • JsonCpp库的使用
  • SQL基础——MySQL的优化
  • SOHO建站
  • 【mysql】SQL语言的概述
  • java03
  • 深入探索Java中的分布式文件系统:从理论到实战
  • LeetCode_sql_day18(1841.联赛信息统计)
  • 维信小程序禁止截屏/录屏
  • React学习day03-components插件安装(仅基于火狐浏览器)、受控表单绑定、在React中获取dom、组件通信(组件间的数据传递)
  • 51单片机-串口通信关于SBUF的问题
  • elementui 表单 tab切换下个光标能不能改成enter键
  • 24数学建模国赛提供助攻(13——灰色系统理论)
  • 611.有效三角形的个数
  • 豆包MarsCode编程助手:让编程更简单
  • 七、场景加载
  • git中的分支是什么?分支有哪些好处?如何建立分支?
  • PyTorch Geometric(torch_geometric)简介
  • 行业首家!百度智能云通过中国信通院「H5 端人脸识别安全能力」测评
  • DORIS - DORIS注意事项(一)
  • C++:类的定义、实例化
  • Explorer++:轻量级高效文件管理器!!
  • 论文阅读:MicroNet: Towards Image Recognition with Extremely Low FLOPs