1.多态的认识
什么是个多态呢?
具有不同函数体的函数可用同一个函数名,这样就可以用一个函数名去调用不同实现内容的函数。
其实函数重载就是多态的现象。
静态多态示例:
#include <iostream>
using namespace std;
int max(int a, int b) //函数重载版本一
{
return a > b ? a : b;
}
int max(int a, int b, int c) //函数重载版本二
{
int m = a > b ? a : b;
return m > c ? m : c;
}
double max(double a, double b) //函数重载版本三
{
return a > b ? a : b;
}
double max(double a, double b, double c) //函数重载版本四
{
int m = a > b ? a : b;
return m > c ? m : c;
}
int main()
{
int a = 5, b = 6, c = 7;
double x = 1.1, y = 1.2, z = 1.3;
cout << a << "," << b << "最大值: " << max(a, b) << endl;
cout << a << "," << b << "," << c << "最大值:" << max(a, b, c) << endl;
cout << x << "," << y << "最大值: " << max(x, y) << endl;
cout << x << "," << y << "," << z << "最大值:" << max(x, y, z) << endl;
system("pause");
return 0;
}
静态多态:函数调用的实参确定函数调用与函数体的匹配关系。
该代码中,只需要调用max函数,但是执行的功能却不同,一个函数的功能是求两个数的大小,一个是求三个数大小,通俗来说,同一个函数名,但是所执行的功能却不同。
2.虚函数
2.1虚函数的概念
在同一个类中,不能定义原型相同的函数,也就是不能定义两个函数名相同并且参数个数和类型也相同以及返回值也相同的函数,否则编译器报错,但是在类的继承中,可以在派生类中定义一个与基类的成员函数,该函数名相同,参数个数,参数类型,返回值都可以完全相同。
示例:
#include <iostream>
using namespace std;
//基类A
class A
{
private:
int a;
public:
A(int x) : a(x) {}
void Show() const { cout << "A::a = " << a << endl; }
};
//派生类B
class B : public A
{
private:
int b;
public:
B(int x, int y) : A(x), b(y) {}
void Show() const { cout << "B::b = " << b << endl; }
};
//派生类C
class C : public A
{
private:
int c;
public:
C(int x, int y) : A(x), c(y) {}
void Show() const { cout << "C::c = " << c << endl; }
};
int main()
{
A* p; //定义基类指针遍历p
A a(1);
B b(1, 2);
C c(1, 3);
p = &a; //指向基类的对象a
p->Show();
p = &b; //指向派生类对象b
p->Show();
p = &c; //指向派生类对象c
p->Show();
system("pause");
return 0;
}
运行结果:
本示例中,定义了一个基类的指针p,和基类的对象a,和两个派生类对象b和c。
p->Show()都调用的是基类A的,那么怎么样只通过
p->Show()
该语句既可以调用基类的版本也可以调用派生类的版本呢?
这就是虚函数起到作用了。
虚函数声明格式: virtual 返回值类型 成员函数名(形参表)
与一般成员函数相比,虚函数的声明增加了关键字virtual,成员函数的实现部分可以在类体内,也可以在类外面,在类外不需要加关键字virtual。
虚函数在基类声明时一定要加关键字virtual,在派生类中声明虚函数时候可以省略
虚函数示例:
#include <iostream>
using namespace std;
//基类A
class A
{
private:
int a;
public:
A(int x) : a(x) {}
virtual void Show() const { cout << "A::a = " << a << endl; }
};
//派生类B
class B : public A
{
private:
int b;
public:
B(int x, int y) : A(x), b(y) {}
void Show() const { cout << "B::b = " << b << endl; }
};
//派生类C
class C : public A
{
private:
int c;
public:
C(int x, int y) : A(x), c(y) {}
void Show() const { cout << "C::c = " << c << endl; }
};
int main()
{
A* p; //定义基类指针遍历p
A a(1);
B b(1, 2);
C c(1, 3);
p = &a; //指向基类的对象a
p->Show();
p = &b; //指向派生类对象b
p->Show();
p = &c; //指向派生类对象c
p->Show();
system("pause");
return 0;
}
运行结果:
与上面代码相比就在基类的Show函数中加了virtual实现的结果却不同
virtual void Show() const { cout << "A::a = " << a << endl; }
那么就在基类的Show函数前面加了virtual这个东西,就能实现不一样的效果,这是因为
基类指针指向派生类对象的时候,会进行指针类型转换,派生类对象的指针转化成基类的指针,所以基类的指针指向的是派生类中的基类部分。
但是虚函数突破了这一限制,派生类的虚函数替换基类的虚函数,这样可以通过基类的指针指向派生类的时候,调用的虚函数其实就是派生类的虚函数。
但由于虚函数采用基类指针指向不同类的对象从而实现多态性,与指针指向具体的对象有关,因此C++规定静态成员函数不能是虚函数,因为静态成员函数不存在对象。
由于指针指向一个具体对象后,不能调用构造函数,因此构造函数也不能是虚函数。
通过函数是基类的对象的引用而实参是不同类的对象,采用虚函数实现多态性的宁外一种形式
示例:
#include <iostream>
using namespace std;
//基类A
class A
{
private:
int a;
public:
//virtual A(int x) : a(x) {} 错 构造函数不能是虚函数
//virtual static void Show() {} 错 静态成员函数不能是虚函数
A(int x) : a(x) {}
virtual void Show() const { cout << "A::a = " << a << endl; }
};
//派生类B
class B : public A
{
private:
int b;
public:
B(int x, int y) : A(x), b(y) {}
void Show() const { cout << "B::b = " << b << endl; }
};
//派生类C
class C : public A
{
private:
int c;
public:
C(int x, int y) : A(x), c(y) {}
void Show() const { cout << "C::c = " << c << endl; }
};
void Show(const A& a)
{
a.Show();
}
int main()
{
A a(1);
B b(1, 2);
C c(1, 3);
Show(a);
Show(b);
Show(c);
system("pause");
return 0;
}
运行结果:
本示例中,函数的形参是基类的对象的引用
void Show(const A& a)
在主函数中通过函数实参调用不同派生类的对象时候,会进行类型转换,如果没有采用虚函数,只能引用基类的部分,但是加了虚函数后,将派生类的虚函数替换成基类的虚函数
3.虚析构函数
析构函数不采用虚函数:
#include <iostream>
using namespace std;
class A
{
public:
~A() { cout << "指向A析构函数" << endl; }
};
class B : public A
{
public:
~B() { cout << "执行B析构函数" << endl; }
};
int main()
{
A* p = new B;
delete p;
system("pause");
return 0;
}
运行结果:
加了虚函数示例:
#include <iostream>
using namespace std;
class A
{
public:
virtual ~A() { cout << "指向A析构函数" << endl; }
};
class B : public A
{
public:
~B() { cout << "执行B析构函数" << endl; }
};
int main()
{
A* p = new B;
delete p;
system("pause");
return 0;
}
4.纯虚函数与抽象类
有时候只是在基类中某一个成员函数声明一个虚函数,是考虑到派生类的需要,只是在基类中预留一个函数名,具体的功能却在派生类去实现。这样就适合将基类的成员函数声明为纯虚函数。
纯虚函数声明格式:
virtual 返回值类型 函数名(参数) = 0;
纯虚函数没有函数体,纯虚函数不能被调用,只是通知编译系统声明一个虚函数,留在派生类中定义。因为纯虚函数是不能被调用的,因此纯虚函数不能建立对象。
在类里面定义的纯虚函数,该类也称为抽象类,抽象类虽然不能定义对象,但是可以定义抽象类的指针,能够指向派生类,然后通过指针指向虚函数,实现了多态性。
纯虚函数与抽象类示例:
#include <iostream>
using namespace std;
//声明抽象类A
class A
{
public:
virtual ~A() {}
virtual void Show() const = 0; //声明纯虚函数
};
//声明派生类B
class B : public A
{
private:
int a;
public:
B(int x) : a(x) {}
void Show() const { cout << "B::a = " << a << endl; }
};
//声明派生类C
class C : public A
{
private:
int b;
public:
C(int x) : b(x) {}
void Show() const { cout << "C::b = " << b << endl; }
};
void Show(const A& a)
{
a.Show();
}
int main()
{
//A a; 错 抽象类不能定义对象
A* p;
B a(1);
C b(2);
Show(a);
Show(b);
p = &a;
p->Show();
p = &b;
p->Show();
system("pause");
return 0;
}
运行结果:
5.纯虚函数应用
编写程序,定义抽象类Shape(形状),由它派生出2个派生类:Circle(圆形)和Rectangle(矩形), 用函数GetArea()返回个各种图形的面积,用函数GetShapeName()返回图形名。
在类Shape(形状)中将函数GetArea()与GetShapeName()设置为纯虚函数.它们的具体定义放在派生类中实现。
代码:
#include <iostream>
using namespace std;
#define PI 3.14
//抽象类
class Shape
{
public:
virtual ~Shape() {}
virtual double GetArea() const = 0; //声明纯虚函数 返回面积
virtual const char* GetShapeName() const = 0;//声明纯虚函数 返回图形
};
//派生类
class Circle : public Shape
{
private:
double radius;
public:
Circle(double a) : radius(a) {}
double GetArea() const //返回面积
{
return radius * radius * PI;
}
const char* GetShapeName() const //返回形状
{
return "圆";
}
};
//派生类
class Rectangle : public Shape
{
private:
double height; //高
double width; //宽
public:
Rectangle(double h, double w) : height(h), width(w) {}
double GetArea() const //返回面积
{
return height * width;
}
const char* GetShapeName() const //返回形状
{
return "矩形";
}
};
int main()
{
Shape* p; //抽象类Shape指针
p = new Circle(1);
cout << p->GetShapeName() << "的面积为:" << p->GetArea() << endl;
delete p;
p = new Rectangle(1, 2);
cout << p->GetShapeName() << "的面积为:" << p->GetArea() << endl;
delete p;
system("pause");
return 0;
}
运行结果:
6.栈的实现
栈是允许在一端进行插入和删除的线性表,栈顶称为Top,栈底称为Bottom
设一个栈有元素(a1,a2, a3…an),则a1是栈底,an是栈顶,按照a1,a2,a3…an顺序入栈,那么出栈的顺序an…a3,a2,a1,栈可以理解为先进后出的线性表。
栈的基本操作:
1.入栈:将一个元素插入到栈顶
2.出栈:删除栈顶元素,返回栈顶元素的值
3.取栈顶:返回栈顶元素的值
栈的具体实现方式分:链式栈和顺序栈
对于顺序栈:
习惯将栈顶top初始化为-1
在这里插入图片描述
入栈:
出栈:
链式栈:
每一个存储空间分数据域与指针域
数据域:存储数据
指针域:存储下一个结点的位置
Node.h
#pragma once
template <typename ElemType>
struct Node
{
ElemType data;
struct Node<ElemType>* next;
Node(ElemType data, struct Node<ElemType>* Link = NULL) //为每一个表数据域与指针域构造
{
this->data = data;
next = Link;
}
};
stack.cpp
#include <iostream>
#include "Node.h"
using namespace std;
#define SIZE 1000
//抽象类
template <typename ElemType>
class Stack
{
public:
virtual ~Stack() {}
virtual bool Push(ElemType e) = 0; //入栈
virtual bool Pop(ElemType& e) = 0; //弹栈
virtual bool GetTop(ElemType& e) = 0; //取栈顶
};
//顺序栈
template <typename ElemType>
class sqStack : public Stack<ElemType>
{
private:
ElemType* Link;
int top; //栈顶
public:
sqStack() //构造 栈顶
{
Link = new ElemType[SIZE]; //构造数组
top = -1; //设置栈顶位置
}
~sqStack() {} //析构
bool Push(ElemType e) //入栈
{
if (top == SIZE) { cout << "栈满" << endl; return false; } //判断栈是否满
else
Link[++top] = e;
return true;
}
bool Pop(ElemType& e) //弹栈
{
if (top == -1) { cout << "栈空" << endl; return false; }
else
e = Link[top--];
return true;
}
bool GetTop(ElemType& e) //取出栈顶
{
if (top == -1) { return false; }
else
e = Link[top];
return true;
}
};
//链式栈
template <typename ElemType>
class LinkStack : public Stack<ElemType>
{
private:
struct Node<ElemType>* top; //栈顶指针
public:
LinkStack() { top = NULL; } //为栈顶指针初始化
~LinkStack() //析构 释放所以内存空间
{
while (top != NULL)
{
Node<ElemType>* p = top;
top = top->next;
delete p;
}
}
bool Push(ElemType e) //入栈
{
top = new Node<ElemType>(e, top);
return true;
}
bool Pop(ElemType& e) //弹栈
{
Node<ElemType>* p = top; //找到当前栈顶位置
e = top->data; //获取栈顶的元素
top = top->next; //栈顶指针指向下一个元素的位置
delete p; //释放上一个栈顶的位置
return true;
}
bool GetTop(ElemType& e) //取栈顶
{
if (top == NULL) { return false; }
else
e = top->data;
return true;
}
};
//菜单选择
void menu()
{
cout << "\n\n\n\n\n";
cout << "\t\t\t\t\t\t\t 1.入栈" << endl << endl;
cout << "\t\t\t\t\t\t\t 2.出栈" << endl << endl;
cout << "\t\t\t\t\t\t\t 3.取栈顶" << endl << endl;
cout << "\t\t\t\t\t\t\t 4.退出" << endl << endl;
}
//栈的操作
void operate(Stack<int>* p)
{
int n;
int e; //取出的元素
int k = 0; //入栈元素初始化
while (1)
{
system("CLS");
menu();
cout << "请选择你要的选项:(1~4):";
cin >> n;
switch (n)
{
case 1: //入栈
cout << "请输入入栈元素的个数:";
cin >> k;
for (int i = 0; i < k; i++)
{
cout << "请输入入栈第" << i + 1 << "个元素";
cin >> e;
if (p->Push(e))
continue;
else
{
cout << "入栈失败!" << endl;
return;
}
}
cout << k << "个元素入栈成功" << endl;
system("pause");
break;
case 2: //出栈
if (k == 0) { cout << "出栈失败请先执行入栈操作" << endl; }
else
{
cout << "出栈顺序:";
for (int i = 0; i < k; i++)
{
if (p->Pop(e))
cout << e << " ";
else
{
cout << "出栈失败!" << endl;
return;
}
}
}
cout << "\n" << k << "个元素入栈成功" << endl;
system("pause");
break;
case 3: //取栈顶
if (p->GetTop(e))
cout << "栈顶元素:" << e << endl;
else
cout << "当前栈顶为空" << endl;
system("pause");
break;
case 4: //退出
cout << "退出成功" << endl;
system("pause");
return;
}
}
}
int main()
{
int e;
int n;
Stack<int>* p = NULL; //定义一个抽象类的指针
cout << "请选择栈(1.顺序栈, 2.链式栈):" << endl;
cin >> n;
switch (n)
{
case 1: //顺序栈
p = new sqStack<int>;
break;
case 2: //链式栈
p = new LinkStack<int>;
break;
}
operate(p); //栈的操作
delete p; //释放栈
cout << "栈释放成功" << endl;
system("pause");
return 0;
}