纯虚拟函数(一)


在〈虚拟函数〉中,将to_string设成virtual了,然而你可能会发现,Role的子类都有fight方法,为什么不将它们提升至父类并设为virtual?可以是可以,不过提升之后,在Role中的方法该写什么呢?空的方法本体?如果是这样的话,不如将它设为纯虚拟函数(pure virtual function),也就是没有任何实现的方法:

class Role {
    ...略

public:
    Role(string name, int level, int blood)
     : name(name), level(level), blood(blood) {}

    virtual void fight() = 0;

    …略
};

这么一来,就可以如下写个doFight了:

...略

void doFight(Role &role) {
    role.fight();
}

int main() { 
    SwordsMan swordsMan("Justin", 1, 1000);
    Magician magician("Magician", 1, 800);

    fight(swordsMan);
    fight(magician);

    return 0;
}

被设为 0 的virtual函数,没有任何实现,是个抽象方法,而拥有抽象方法的类,是个抽象类(abstract class),不能用来实例化:

Role role("Justin", 1, 1000); // error: cannot declare variable 'role' to be of abstract type 'Role'

因此,也不能如下指定:

Role role = swordsMan;

也就是说,因为抽象类不是个完整的类定义,只用来定义参考或指针,而继承抽象类的子类,一定要重新定义抽象方法,否则该子类也会是抽象类,无法用来实例化。

来看看抽象方法与抽象类的另一个应用,如果要你开发一个猜数字游戏,会随机产生 0 到 9 的数字,使用者输入的数字与随机产生的数字相比,如果相同就显示「猜中了」,如果不同就继续让使用者输入数字,直到猜中为止。

这程序有什么难的?相信现在的你也可以实现出来:

#include <iostream>
#include <string> 
#include <chrono>
#include <random>
#include <functional>
using namespace std;

function<int()> rand(int from, int to) {
    unsigned seed = chrono::system_clock::now()
                        .time_since_epoch().count(); // 随机数种子
    default_random_engine uniform(seed);             // 产生均匀分布随机数的引擎
    uniform_int_distribution<int> dist(from, to);    // 范围为 0 到 9 均匀分布
    return bind(dist, uniform);                      // 绑定引擎与范围
}

int main() { 
    function<int()> randZeroToNine = rand(0, 9);

    int number = randZeroToNine();
    int guess = 0;
    do {
        cout << "输入数字:";
        cin >> guess;
    } while(guess != number);
    cout << "猜中了!" << endl;

    return 0;
}

不过,需求中并没有说要在文本模式下执行这个游戏,也就是说获取使用者输入、显示结果的环境未定,但你负责的这部份还是要先实现,怎么办呢?可以先设计个抽象类:

class GuessGame {
public:
    void go() {
        function<int()> randZeroToNine = rand(0, 9);

        int number = randZeroToNine();
        int guess = 0;
        do {
            this->print("输入数字:");
            guess = this->nextInt();
        } while(guess != number);
        println("猜中了!");
    }

    void println(string text) {
        this->print(text + "\n");
    }

    virtual void print(string text) = 0;
    virtual int nextInt() = 0;

    virtual ~GuessGame() = default;
};

对于文本模式中的游戏,可以继承GuessGame

...略

class ConsoleGame : public GuessGame {
public:
    void print(string text) {
        cout << text;
    }

    int nextInt() {
        int guess = 0;
        cin >> guess;
        return guess;
    }
};

int main() { 
    GuessGame &&game = ConsoleGame();
    game.go();

    return 0;
}

一个执行的结果如下:

输入数字:3
输入数字:5
输入数字:6
输入数字:7
输入数字:8
猜中了!




展开阅读全文