目录
学习内容:
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;
}