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;
}