《C++语言教程》12章 多态的应用


基类的虚函数在派生类中再定义称为函数的“覆盖(overriding)”,与“重载(overload)”是不同的2个概念,不要混淆。重载对类适用,对普通函数也适用,重载函数的参数一定是不相同的。覆盖只适用于类,并且是基类与派生类之间的重定义,其参数甚至返回值必须完全相同。

虚函数或纯虚函数自身并不实现任何功能,各个不同的类在继承时才实现不同的功能,因此,同一个虚函数在执行时具有了多种不同功能,称之为“多态”。

小雅以前在为单位面试新人时,有很多人对面向对象还很模糊,便自称精通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;
}

二、队列和堆栈

队列和堆栈都是链表类型(可以参考“C语言教程”中的22章),它和数组不同的是个数是不定的,因此必须用指针来“记住”开始、末尾和下一个的位置。因此基类中有三个指针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;
}