用 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*>;
。
我正在创建一个控制台应用程序并且需要一个菜单。 此菜单可以 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*>;
。