C/C++

Архитектурные трабблы при использовании ООП

Порядка месяца назад задался идеей написать класс для консольного меню, чтобы не использовать бесконечные свич кейсы. Идея простая: класс хранит вектор указателей на ячейки "Case"(рис 1),каждая из которых в свою очередь хранит заголовок пункта меню, код клавиши и указатель на вызываемую функцию. Написал рабочий вариант, обрадовался, стал пользоваться. Но теперь пришла идея сделать класс отдельной библиотекой, и улучшить саму систему. Я посчитал, что Case, который хранит и указатель на функцию, и указатель на подменю, это неправильно, ведь функциональный кейс все равно никогда не будет подменю. Поэтому я создал абстрактный базовый класс Case, от которого унаследовал 2 разных Case (рис 2)Как я и сказал, в самом классе Menu список пунктов представлен как вектор указателей vector<Case* > menu; ведь указатель базового класса может хранить наследника. Но очень скоро я столкнулся с проблемой:
В первой версии вывод меню был в цикле (рис 3)где current это тот же вектор кейсов, то есть мейн меню или одно из подменю. Но в новом варианте с Case в виде базового класса, я уже не могу получить доступ к полям с указателем на функцию или подменю, ведь current не знает, какой именно наследник в нем хранится. Да и к полям, которые определены в самом Case доступа тоже нет, т.к. класс абстрактный. И вот самая суть проблемы: нужно создать коллекцию, которая будет хранить 2 разных типа кейсов, но в то же время при обходе этой коллекции нужно во-первых иметь доступ к полю с кодом символа, чтобы найти нужный кейс, а во-вторых, в зависимости от типа этого кейса выполнить то или иное действие (вызвать переданную функцию, либо вызвать подменю).
Изучать ООП я начал не так давно, но мне просто не у кого спросить совета, так что буду рад любым идеям и наставлениям
Почему нет доступа к полям базового Case? Абстрактность класса или структуры никак на это не влияет. Влияет модификатор доступа. Он был public, а стал protected, поэтому и доступ пропал. Здесь апологеты ООП, которые держат у себя дома икону Страуструпа, посоветовали бы написать ещё немного ритуального кода (они вообще любят писать ритуальный код, из него состоит более 90% их проектов, что, как кажется авторам, придаёт им серьёзный вид):
 public:
char getCode() { return code; }

protected:
char code;
и т.п.

Я бы сказал, что если эти поля не меняются за время жизни структуры, то проще их объявить публичными и const, и тогда будет и доступ к полю, и несанкционированные изменения будут исключены.
 public:
const char code;

А что касается полей производных классов, то обрабатывающий код общего вида и не должен ничего о них знать. Нужно сделать полиморфный метод в базовом классе, а в производных - переопределить его, чтобы он выполнял то, что нужно. Например:
 public:
virtual bool isButton(char c) = 0;
В том наследнике, где char code, возвращать
 public:
virtual bool isButton(char c) {
return code == c;
}
А во втором - это тебе решать, каким будет поведение, если там не хранится код. :-)
Метод придётся сделать virtual, т.к. во время компиляции неизвестно, в каком элементе массива указатель на какого наследника лежит.
Фёдор Алексеич
Фёдор Алексеич
87 571
Лучший ответ
Серёга Беляев Согласен, не обратил внимания, что в Case поля были приватными.
Но возможно ли сделать полиморфное поле? Просто получается так, что мне нужно, чтобы 1 наследник хранил указатель на функцию, а другой - указатель на меню? Может в таком случае удобнее вместо указателя использовать Function<>? Сейчас пробую добиться этого полиморфизма через шаблоны, но пока ещё разбираюсь
>я уже не могу получить доступ к полям с указателем на функцию или подменю, ведь current не знает, какой именно наследник в нем хранится
А зачем ему это знать? Полиморфизм изобрели именно для того, чтобы ничего не знать, и все работало как надо. Вызывай абстрактные методы и дай потомкам сделать все как надо.
AA
Aleksandr Awdonin
90 746
Возможно не совсем удачный пример, в котором все немного закручено. Тут в принципе и ненужны классы и наследования. Достаточно в одной структуре держать ее тип, и указатель типа войд (на любой нужный вам обьект). А в самой меню в зависимости от типа этот указатель так или иначе интерпретируется.
Ps: в коде естесствено нет освобождения памяти от указателей.
 #include    
#include
using namespace std;
typedef void(*ptr_fu)();
struct Menu
{
string menu = "horses and dindin";
};
void foo()
{
cout
На мой взгляд сама постановка вопроса "задался идеей написать класс для консольного меню, чтобы не использовать бесконечные свич кейсы" не совсем правильная.
Во-первых, в реальных программах такая штука, как текстовое меню, практически не используется.
Во-вторых, попробуйте посмотреть на проблему меню с точки зрения реализации переходов между состояниями конечного автомата. Тут очень в тему будут массивы указателей на функции и состояние в качестве индекса.
Andros Vip
Andros Vip
9 624