C++多态的应用
<上一节
下一节>
基类的虚函数在派生类中再定义称为函数的“覆盖(overriding)”,与“重载(overload)”是不同的两个概念,不要混淆。重载对类适用,对普通函数也适用,重载函数的参数一定是不相同的。覆盖只适用于类,并且是基类与派生类之间的重定义,其参数甚至返回值必须完全相同。
虚函数或纯虚函数自身并不实现任何功能,各个不同的类在继承时才实现不同的功能,因此,同一个虚函数在执行时具有了多种不同功能,称之为“多态”。
笔者以前在为单位面试新人时,有很多人对面向对象还很模糊,便自称精通C++,这是不可取的。理解了类、抽象类的思想还只能算入门,只有在不断实践中掌握更多的设计方法以及各种设计Pattern,才能算真正的C++程序员。
因这部分内容比较重要,本节不介绍新内容,只举例来加深理解。
设计2个派生类,1个是长方形rectangle、1个是三角形triangle。要点是对getArea函数实装。
长方形的面积为: 1540
三角形的面积为: 450
派生类里面要实现2个功能,即增加节点、删除节点。增加时首先new一个节点并对num赋值,然后修改链表的三个指针。而删除时要先修改链表的三个指针,然后再删除节点。
队列和堆栈的不同处是:队列是先进先出、后进后出;而堆栈则是先进后出、后进先出。这就是修改三个链表指针时要注意的地方。这需要有一点数据结构方面的知识。
队列: 111 222 333
堆栈: 333 222 111
虚函数或纯虚函数自身并不实现任何功能,各个不同的类在继承时才实现不同的功能,因此,同一个虚函数在执行时具有了多种不同功能,称之为“多态”。
笔者以前在为单位面试新人时,有很多人对面向对象还很模糊,便自称精通C++,这是不可取的。理解了类、抽象类的思想还只能算入门,只有在不断实践中掌握更多的设计方法以及各种设计Pattern,才能算真正的C++程序员。
因这部分内容比较重要,本节不介绍新内容,只举例来加深理解。
求面积
基类里定义三个函数:设置区域数据setData()、取区域数据getData、求面积(虚函数)getArea。所谓数据,定义2个成员变量dim1和dim2,对于长方形来说是长和宽,对于三角形来说是底和高。设计2个派生类,1个是长方形rectangle、1个是三角形triangle。要点是对getArea函数实装。
#include <iostream>
using namespace std;
class area {
double dim1, dim2;
public:
//设置数据
void setData(double d1, double d2) {
dim1 = d1, dim2 = d2;
}
//取数据
void getData(double& d1, double& d2) {
d1 = dim1, d2 = dim2;
}
//求面积
virtual double getArea() = 0; //纯虚函数
};
//长方形
class rectangle : public area {
public:
double getArea() {
double d1, d2;
getData(d1, d2);
return d1 * d2 ;
}
};
//三角形
class triangle : public area {
public:
double getArea() {
double d1, d2;
getData(d1, d2);
return d1 * d2 / 2 ;
}
};
int main ( )
{
area *p;
rectangle r; //长方形类的实例
triangle t; //三角形类的实例
r.setData(35, 44);
t.setData(36, 25);
p = &r;
cout << "长方形的面积为: " << p->getArea() << endl;
p = &t;
cout << "三角形的面积为: " << p->getArea() << endl;
return 0;
}
运行结果:长方形的面积为: 1540
三角形的面积为: 450
队列和堆栈
队列和堆栈都是链表类型,它和数组不同的是个数是不定的,因此必须用指针来“记住”开始、末尾和下一个的位置。因此基类中有三个指针head、tail、next成员变量。还有一个成员变量num用来表示当前插入的值。另外定义2个纯虚函数store()和retrieve()来表示存贮(即增加)一个元素、处理(减少)一个元素,这是要编程的要点。派生类里面要实现2个功能,即增加节点、删除节点。增加时首先new一个节点并对num赋值,然后修改链表的三个指针。而删除时要先修改链表的三个指针,然后再删除节点。
队列和堆栈的不同处是:队列是先进先出、后进后出;而堆栈则是先进后出、后进先出。这就是修改三个链表指针时要注意的地方。这需要有一点数据结构方面的知识。
#include <iostream>
#include <cstdlib>
#include <cctype>
using namespace std;
class list {
public:
list *head; //头指针
list *tail; //尾指针
list *next; //下一个指针
int num; //当前节点的值
list() { head = tail = next = NULL; } //构造函数
virtual void store(int i) = 0; //增加节点
virtual int retrive() = 0; //删除节点
};
//队列
class queue : public list {
public:
//增加节点
virtual void store(int i) {
list *item = new queue;
if (!item) {
cout << "内存分配失败!" << endl;
exit(1);
}
item->num = i;
//链表末尾插入
if (tail) tail->next = item;
tail = item;
item->next = NULL;
if (!head) head = tail;
}
//取值并删除节点
int retrive() {
int i;
list *p;
if (!head) {
cout << "链表已经为空" << endl;
return 0;
}
//从链表头部删除
i = head->num;
p = head;
head = head->next;
delete p;
return i;
}
};
//堆栈
class stack : public list {
public:
//增加节点
virtual void store(int i) {
list *item = new stack;
if (!item) {
cout << "内存分配失败!" << endl;
exit(1);
}
item->num = i;
//链表头部插入
if (head) item->next = head;
head = item;
if (!tail) tail = head;
}
//取值并删除节点
int retrive() {
int i;
list *p;
if (!head) {
cout << "链表已经为空" << endl;
return 0;
}
//从链表头部删除
i = head->num;
p = head;
head = head->next;
delete p;
return i;
}
};
int main ( )
{
list *p;
//队列的演示
queue q;
p = &q;
p->store(111), p->store(222), p->store(333);
cout << "队列: ";
cout << p->retrive() << " ";
cout << p->retrive() << " ";
cout << p->retrive() << endl;
//堆栈的演示
stack s;
p = &s;
p->store(111), p->store(222), p->store(333);
cout << "堆栈: ";
cout << p->retrive() << " ";
cout << p->retrive() << " ";
cout << p->retrive() << endl;
return 0;
}
运行结果:队列: 111 222 333
堆栈: 333 222 111
<上一节
下一节>
