用 C++ (OOP) 制作菜单

Making a menu in C++ (OOP)

我正在创建一个控制台应用程序并且需要一个菜单​​。 此菜单可以 link 到下一个菜单或执行操作。 执行此操作的良好 OOP 方法是什么?

我见过很多人都是这样做的,但并不是真正可维护的

TJ::clearScreen(); // Function I wrote to clear the console
TJ::breakSection('='); // Creates a line of "===...==="
std::cout << "Main menu" << std::endl;
TJ::breakSection();
std::cout << "[1] abcd" << std::endl;
std::cout << "[2] hrhdd" << std::endl;
std::cout << "[3] aeshsrh" << std::endl;
TJ::breakSection('=');

std::cin >> choice;

switch(choice) {
    case 1:
        doThing1();
        break;
    case 2:
        doThing1();
        break;
    case 3:
        doThing1();
        break;
}

有没有办法让它看起来像这样?

menu.addChoice(functionA);
menu.addChoice(menuB);
menu.addChoice(functionC);
menu.addChoice(menuD);
menu.display();

因此,如果用户选择 option A(通过在控制台中键入“A”),它将执行 functionA。如果用户选择option B,它会显示menuB。 我已经看过像你在下面看到的那样做的事情。但是我不知道如何允许同时选择功能和菜单。

class Menu {
private:
    std::string title;

    class Item {
    private:

    public:
        Item();
        ~Item();

        int run();
        int display();
    };

    std::vector<Item> Choices;
public:
    Menu();
    ~Menu();

    Menu& addChoice();
    Menu& display();
};

您似乎在寻找函数指针。 您可以像这样将它们存储在向量中:

std::vector<void (*) ()> options{&functionA , &displayMenuB, &functionC, &displayMenuD}; 
options[0](); // Executes function A
options[1](); // Shows menu B

唯一的问题是displayMenuB这里需要是一个函数,而不是一段原始数据...(我没有完全理解你的问题,但如果你只是想显示菜单B到控制台它可能是一个返回 void 的函数。

当然,您需要将所有这些都包装在一个 class 中并适当地封装它,但我希望您能理解。

注意:函数指针只能指向无状态函数。如果你想存储一个仿函数(基本上是一个带有一些数据但可以像函数一样调用的对象),你应该使用 std::function.

我已经付出了一些努力,并为您的问题创建了一种菜单基础结构。它可以存储具有相同参数列表的函数。另外,我的实现使用std::map,所以比std::vector索引更方便快捷。
Menu.h

#pragma once

#include <functional>
#include <string>
#include <map>

using std::function;
using std::string;
using std::map;

template <class ...Types>
class Menu
{
private:
    template <class ...Types>
    class Item
    {
    private:
        function<void(Types...)> func;
        string desc;
    public:
        Item() = default;
        Item(function<void(Types...)> func, string desc) : func(func), desc(desc) {}
        void setDescription(string desc) { this->desc = desc; }
        string getDescription() { return desc; }
        void run(Types ... params) { func(std::forward<Types>(params)...); }
    };
private:
    string text;
    map<string, Item<Types...>> choices;
public:
    void addChoice(string key, function<void(Types...)> func, string desc = "");
    void setText(string txt) { text = txt; }
    void setDescription(string key, string desc);
    void run(string key, Types ... params);
    void display();
};

Menu.cpp

#include "Menu.h"

#include <iostream>

using std::cout;
using std::endl;

template<class ...Types>
void Menu<Types...>::addChoice(string key, function<void(Types...)> func, string desc)
{
    Item<Types...> item{ func, desc };
    this->choices[key] = item;
}

template<class ...Types>
void Menu<Types...>::setDescription(string key, string desc)
{
    this->choices[key].setDescription(desc);
}

template<class ...Types>
void Menu<Types...>::run(string key, Types ... params)
{
    if (choices.find(key) != choices.end())
        this->choices[key].run(std::forward<Types>(params)...);
}

template<class ...Types>
void Menu<Types...>::display()
{
    cout << this->text << endl;
    for (auto& c : this->choices)
        cout << "\t" << c.first << ":  " << c.second.getDescription() << endl;
    cout << endl;
}

template class Menu<>;

Main.cpp

#include <iostream>

#include "Menu.h"

using std::cout;
using std::cin;
using std::endl;

void funcA();
void funcB();
void funcC();

int main()
{
    Menu<> menu;
    menu.setText("Choose:");
    menu.addChoice("A", funcA, "A description");
    menu.addChoice("B", funcB /*no description*/);
    menu.addChoice("C", funcC /*no description*/);
    menu.setDescription("C", "C description");
    menu.display();

    string s;
    std::getline(cin, s);
    menu.run(s);

    return 0;
}

void funcA()
{
    cout << "Function A" << endl;
}

void funcB()
{
    cout << "Function B" << endl;
}

void funcC()
{
    cout << "Function C" << endl;
}

输出:

Choose:
    A:  Function A
    B:
    C:  Function C
A
Function A

不要忘记为任何其他签名添加 template class Menu<*types*>;