Указатель на неопределенный класс ... ?
- Войдите на сайт для отправки комментариев
Вс, 24/10/2021 - 18:23
Можно ли, и как, создать указатель на класс, если класс заранее неизвестен ?
Т.е. создать указатель на класс "вообще" ?
С заранее известными все понятно:
HardwareSerial *sp;
sp = &Serial;
sp->prinln("Это Serial");
А как быть если класс заранее неизвестен и может меняться программно (задаваться пользователем) ?
нельзя. Только просто указатель на "нечто" void
Жаль. В других языках это реализуемо.
Можно, конечно. В пределах разумного. Только Вы неверно формулируете задачу, потому не понимаете как её решить. Попробуйте сформулировать вопрос более чётко.
Указатель на "что-нибудь" объявляется очень просто : void *ptr; Но это жуткий говнокод и такой приём оправдан только в очень редких случаях. Чаще всего, если такое потребовалось, значит программа неверно спроектирована.
Иметь указатель на "класс вообще" вещь абсолютно бесполезная. Что Вы с ним будете делать? А вот указатель "на любой класс, у которого есть такой-то метод (такие-то методы)" - это без проблем, это общая стандартная практика. Делайте на здоровье.
Задача:
Есть кнопка, по нажатию которой выполняется какое-то заранее неизвестное действие. Какое - задается пользователем в настройках (после компиляции). Есть несколько объектов разных классов, которые могут выполнять эти действия. Пользователь выбирает объект (по его имени) и функцию в объекте (тоже по имени). Имена заданы в самом классе. После настройки, при нажатии на кнопку должна выполниться заданная функция заданного объекта. Настройки могут изменяться во время работы программы.
В смысле "на любой класс, у которого есть такой-то метод (такие-то методы)" ? Классы свои, я могу задать в них одинаковые (по имени) методы.
Номер выбранного пользователем действия (сценария) в int, и дальше свитч-кейс на кнопке, не??
В программе до десятка объектов + их функции. Количество возможных сценариев (не все практически имеют смысл) будет огромным. Но идея интересная, если не получится по-другому ....
В программе до десятка объектов + их функции. Количество возможных сценариев (не все практически имеют смысл) будет огромным. Но идея интересная, если не получится по-другому ....
Десяток объектов - фигня. Реализуйте каждый объект экземпляром класса, организуйте их в массив и будет вам щщастье.
Либо, если каждый объект уникального класса, то таки свитч-кейс
Автор, зачем такие заморочки? Если всё равно, после в настройках всё определяете. Ну определите какие то начальные условия переменных, а потом меняйте их сколько угодно. Согласен с Rumata. Хотя если это лабораторка, тогда препод попал в точку.
Каждый объект и так реализован как экземпляр класса, но классы разные, в один массив их не загонишь. Количество объектов класса тоже заранее не известно. Известно только максимально возможное значение.
В смысле "на любой класс, у которого есть такой-то метод (такие-то методы)" ? Классы свои, я могу задать в них одинаковые (по имени) методы.
Так какого рожна Вам ещё надо? Так и делайте. В чём проблема?
Или Вы не знаете как сделать указатель "на любой класс, у которого есть такой-то метод (такие-то методы)"?
Это не лабораторка :) Последнюю "лабу" я делал лет 40 назад :)
Это контроллер. Куча устройств 1-wire, RS485, плюс Bluetooth, Wi-Fi, GSM. Пользователь определяет, что делать, если датчик показывает "такую-то" температуру. То-ли включить / выключить что-то, то ли отправить СМС.
Или Вы не знаете как сделать указатель "на любой класс, у которого есть такой-то метод (такие-то методы)"?
Не знаю. Подскажите.
Это не лабораторка :) Последнюю "лабу" я делал лет 40 назад :)
Это контроллер. Куча устройств 1-wire, RS485, плюс Bluetooth, Wi-Fi, GSM. Пользователь определяет, что делать, если датчик показывает "такую-то" температуру. То-ли включить / выключить что-то, то ли отправить СМС.
Тогда это действительно проблема с архитектурой программы. Тут не нужны указатели на "неведомые" классы. Имхо, конечно, я не настаиваю..
Не знаю. Подскажите.
Не ожидал. Вы так смело спорили о том, что такое ООП :-)
Вот пример, разбирайтесь
// Общий класс в котором просто перечислены все методы, // которые есть у всех конкретных классов // При этом whoAreYou будет общим для всех конкретных классов // а method1 и method2 будут у каждого свои struct CGlobal { virtual void method1(void) = 0; virtual void method2(void) = 0; void whoAreYou(void) { method1(); method2(); } }; // Первый конкретный класс struct KakaFirst : public CGlobal { void method1(void) { Serial.println("Это KakaFirst::method1"); } void method2(void) { Serial.println("Это KakaFirst::method2"); } }; // Второй конкретный класс struct KakaSecond : public CGlobal { void method1(void) { Serial.println("Это KakaSecond::method1"); } void method2(void) { Serial.println("Это KakaSecond::method2"); } }; void setup(void) { Serial.begin(115200); // делаем указатели на "какой-нибудь класс" // и создаём объекты обоих конкретных классов CGlobal * ptr1 = new KakaFirst(), * ptr2 = new KakaSecond(); // Проверяем ptr1->whoAreYou(); ptr2->whoAreYou(); } void loop(void) {}Либо ты делаешь базовый класс и вызываешь методы дочерних по желанию, но тут не дельфи, rtti даже близко не пахнет, либо, что проще на контроллерах, заводишь указатели на функции и назначаешь их обработчиками кнопок (их можно переопределять по желанию), а внутри вызывай методы какого хочешь класса. Так можно сделать "плавающие" кнопки, повесить на любую кнопку любой отклик "на лету".
Другими словами вы создаете указатель на базовый класс, а затем присваиваете ему адрес объекта производного класса ?
class parent { }; class child: public parent { public: child(); }; child::child(){} child myChild(); parent *pp; pp = myChild;Другими словами вы создаете указатель на базовый класс, а затем присваиваете ему адрес объекта производного класса ?
Главное, чтобы в базовом классе были определены ВСЕ функции которые могут быть вызваны в дочерних. Пусть даже они будут pure virtual.
Ага. И созданный таким образом объект вполне законно является и объектом типа CGlobal, и объектом типа KakaFirst. Это и есть полиморфизм в данном случае.
Только помни, что по указателю на базовый класс нельзя вызвать функции дочерних, не описаных в базовом
Да, создавая дочерний класс с такими-же функциями, Вы переопределяете функции родительского класса. Это я уже пробовал, но в базовом классе нужно создать много "пустых" процедур и функций, т.к. его наследники СЛИШКОМ разные :(
Я думал о чем-то типа:
class parent {}; class child: public parent{}; parent(child).function();поздравляю. По ООП тебе твёрдая 2.
Да, создавая дочерний класс с такими-же функциями, Вы переопределяете функции родительского класса. Это я уже пробовал, но в базовом классе нужно создать много "пустых" процедур и функций, т.к. его наследники СЛИШКОМ разные :(
Вы мой пример видели? Что там по Вашему такое
Если думаете, что что бы место заполнить, то таки нет.
Вспоминаю #23 (и продолжение)- это правда Вы были? Или Ваш экаунт взломали?
С этим я пока не разобрался :(
Так Вы попробуйте переставить местами - сначала разбирайтесь, а потом посты пишите.
Если бы я сам во всем разобрался, я бы не спрашивал :)
А для чего я Вам пример писал? Уже начинаю жалеть потраченного времени :-(
то, что нужно, называется интерфейс, в #15 вам показал Евгений Петрович, как надо. Может, в современном си ещё более удобные способы есть, уж не глядел я.
А для чего я Вам пример писал? Уже начинаю жалеть потраченного времени :-(
Сорри, отвлекают постоянно :(
Если Вам угодно исполнять какие-то операции по хотелкам юзера, можно и без классов обойтись. Вот пример, запускайте, смотрите. Только сразу предупреждаю, что это
извращениеособенная ориентация.static const char * operatios = "+-*/"; static const double first = 1234, second = 321; static double add(void) { return first + second; } static double sub(void) { return first - second; } static double mul(void) { return first * second; } static double div(void) { return first / second; } typedef double (* OpFunc)(void); static const OpFunc functions[] = { add, sub, mul, div }; void prompt(void) { Serial.println("Вводим операцию: + - * или /"); } void setup(void) { Serial.begin(115200); Serial.print("Первое число: "); Serial.println(first); Serial.print("Второе число: "); Serial.println(second); prompt(); } void loop(void) { if (! Serial.available()) return; const char * fRes = strchr(operatios, Serial.read()); if (fRes) { Serial.println(functions[fRes - operatios]()); prompt(); } }Если Вам угодно исполнять какие-то операции по хотелкам юзера, можно и без классов обойтись.
Я бы хотел этого избежать (хотелок юзера), но пока не знаю как :( Контроллер не имеет заранее известного количества устройств, хотя я УВЕРЕН, что на практике их будет не более трех-пяти, причем у всех одинаковый состав - датчики температуры (2...4), датчики рН (0..2), исполнительный блок (1).
А с чего вы вообще вообразили, что можете создать такой контроллер? С вашими-то нулевыми знаниями?
..... заводишь указатели на функции и назначаешь их обработчиками кнопок (их можно переопределять по желанию), а внутри вызывай методы какого хочешь класса. Так можно сделать "плавающие" кнопки, повесить на любую кнопку любой отклик "на лету".
У меня реализован такой метод для кнопок (поскольку их заранее известное количество), но там однотипные указатели: typedef void (*pFunc)();
void btnSystem(){wnd[0].draw();} void btnAqua(){wnd[1].draw();} class BTN { public: BTN(); uint16_t x1,y1,x2,y2; uint16_t width; uint8_t state; //0-Off,1-On,2-Auto,3-Touch String icon; String name; bool enable; void init(uint16_t bx, uint16_t by, uint16_t bw, String img, String nm, bool en,uint8_t bs, pFunc bf); void draw(); pFunc pBtnTouch; }; BTN::BTN(){ btnCount++; } void BTN::init(uint16_t bx, uint16_t by, uint16_t bw, String img, String nm, bool en, uint8_t bs, pFunc bf){ x1 = bx; y1 = by; x2 = x1+bw; y2 = y1+BTN_HEIGHT; width = bw; icon = img; name = nm; enable = en; state = bs; pBtnTouch = bf; } void BTN::draw(){ uint8_t x=x1+4; lcd.setColor(BTN_COLOR); lcd.setBackColor(BTN_COLOR); lcd.setFont(Icon); lcd.fillRoundRect(x1,y1,x2,y2); lcd.setColor(BTN_OUTLINE_COLOR); lcd.drawRoundRect(x1,y1,x2,y2); if(enable) lcd.setColor(BTN_FONT_COLOR); else lcd.setColor(GRAY); if(state==3){ if(icon != ""){ lcd.setFont(Icon); lcd.print(icon,x,y1+4); x+=42; } if(name != ""){ lcd.setFont(BTN_FONT); lcd.print(name,x,y1+(BTN_HEIGHT-lcd.getFontYsize())/2); } } else lcd.print(btnState[state],x,y1+(BTN_HEIGHT-lcd.getFontYsize())/2); } BTN btn[BTN_COUNT];При нажатии на кнопку (тачскрин) "открывается" определенное окно (или другие действия, для кнопок ручного управления).
Ну это не первый контроллер :) Но там программу писал не я.
https://reefcentral.ru/articles/151/6759/
Ну и такое еще:
https://reefcentral.ru/articles/151/7189/
Прочитай любое базовое введение в классы и зачем они нужны. Там всё есть.
Вот пример, разбирайтесь
// Общий класс в котором просто перечислены все методы, // которые есть у всех конкретных классов // При этом whoAreYou будет общим для всех конкретных классов // а method1 и method2 будут у каждого свои struct CGlobal { virtual void method1(void) = 0; virtual void method2(void) = 0; void whoAreYou(void) { method1(); method2(); } }; // Первый конкретный класс struct KakaFirst : public CGlobal { void method1(void) { Serial.println("Это KakaFirst::method1"); } void method2(void) { Serial.println("Это KakaFirst::method2"); } }; // Второй конкретный класс struct KakaSecond : public CGlobal { void method1(void) { Serial.println("Это KakaSecond::method1"); } void method2(void) { Serial.println("Это KakaSecond::method2"); } }; void setup(void) { Serial.begin(115200); // делаем указатели на "какой-нибудь класс" // и создаём объекты обоих конкретных классов CGlobal * ptr1 = new KakaFirst(), * ptr2 = new KakaSecond(); // Проверяем ptr1->whoAreYou(); ptr2->whoAreYou(); } void loop(void) {}Вот эту строку можете объяснить ?
voidwhoAreYou(void) { method1(); method2(); }Вот эту строку можете объяснить ?
voidwhoAreYou(void) { method1(); method2(); }Самая обыкновенная функция.
Может, так понятнее?
void whoAreYou(void) { method1(); method2(); }Хм ... это я перегрелся :) Я же сам так часто пишу, не построчно :) Сорри.
Остальное вроде понятно.
Хочу еще попробовать создать ОДИН класс для всего (собственно "все" это 5 классов), который состоит из указателей на другие классы. Что-то такое:
class ALL { Bluetooth *bt; GSM *gsm; WiFi *wf; ow ow1[20]; }Тогда по идее к любому устройству можно будет обращаться через ALL.bt.read(); или там ALL.ow1[12].write();
Лучше не класс, а структуру :)
Лучше не класс, а структуру :)
1. В чём, по-Вашему, разница между классом и структурой?
2. Что было у меня в примере?
class ALL { Bluetooth *bt; GSM *gsm; WiFi *wf; ow ow1[20]; }Тогда по идее к любому устройству можно будет обращаться через ALL.bt.read(); или там ALL.ow1[12].write();
Насчёт "по идее" не знаю, может и можно, а на практике - так обращаться нельзя :-(
Почему ? Такой метод работает хорошо, ну по крайней мере у меня работает хорошо, в ЧАСТНОМ проекте.
Если у меня есть класс, в котором много объектов, которые обращаются к функциям одного и того-же класса, то зачем мне все функции этого класса дублировать ? В своем классе я просто указываю ссылку на класс с процедурами / функциями.
class cDS18B20 { public: cDS18B20(); uint8_t rom[8]; uint8_t data[13]; uint8_t group=0; uint8_t num=0; float therm=0; private: OneWire *ow; }; cDS18B20::cDS18B20(){ ow = &OW; }Объектов класса cDS18B20 много, но они все "висят" в одной сети - OneWire и используют функции / процедуры класса сети.
Это плохо ? :(
Да,
Там еще один класс будет добавлен, сеть другого типа.
Немного не по теме ...
class XXX { public: XXX(); }; XXX::XXX(){ pinMode(ppp, OUTPUT); digitalWrite(ppp, HIGH); }процедуры pinMode и digitalWrite не работают, т.е. ВООБЩЕ ничего не происходит. Пин так и остается INPUT.
Почему ? Такой метод работает хорошо, ну по крайней мере у меня работает хорошо, в ЧАСТНОМ проекте.
Т.е. Вы хотите сказать, что при описании
class ALL { Bluetooth *bt; GSM *gsm; WiFi *wf; ow ow1[20]; }Вы в своём частном проекте используете конструкцию
и оно
работает хорошо
???
Боюсь, что Вы единственный, у кого это хорошо работает. У всех остальных такое просто не будет компилироваться :-(
Немного не по теме ...
class XXX { public: XXX(); }; XXX::XXX(){ pinMode(ppp, OUTPUT); digitalWrite(ppp, HIGH); }процедуры pinMode и digitalWrite не работают, т.е. ВООБЩЕ ничего не происходит. Пин так и остается INPUT.
Привыкайте выкладывать скетч полностью. Я же Вам полностью выкладывал - бери и запускай. Так чего ж Вы мне огрызки суёте?
В этом кусочке я, например, вообще не вижу с чего бы ему работать. А додумывать за Вас что там ещё написано ... мало ли что я додумаю.
У тя область видимости класса XXX скорее всего, глобальная
Весь класс целиком. Рабочий.
String charParity[3] = {"N","O","E"}; class HC05 { public: HC05(HardwareSerial &portBT); bool init(); String name; String address; uint16_t baud; uint8_t stopBit;; uint8_t parity; uint8_t mode = UART; bool debug; void clr(); bool empty(); void setMode(uint8_t md); String readStr(); String at(String cmd); void parse(String s); uint32_t color; private: HardwareSerial *port; }; HC05::HC05(HardwareSerial &portBT){ port = &portBT; debug = false; } bool HC05::init(){ pinMode(BT_PRG, OUTPUT); digitalWrite(BT_PRG, LOW); pinMode(BT_RST, OUTPUT); digitalWrite(BT_RST,LOW); delay(100); digitalWrite(BT_RST, HIGH); delay(100); port->begin(38400); delay(1000); if(at("AT").indexOf("OK") != -1){ color = LIME; return true; } else { color = GRAY; return false; } } void HC05::clr(){ while(port->available() > 0) port->read(); } bool HC05::empty(){ if(port->available()) return false; else return true; } void HC05::setMode(uint8_t md){ if(md == AT) { digitalWrite(BT_PRG, HIGH); mode = AT; } if(md == UART) { at("AT+RESET"); digitalWrite(BT_PRG, LOW); mode = UART; } delay(500); } String HC05::readStr(){ long timing; String s=""; char c; timing = millis(); while(millis()-timing < TIME_OUT) while(port->available() > 0){c = port->read(); s+=c;} if(debug) Serial.print("BT->"+s); return s; } String HC05::at(String cmd){ if(mode != AT) setMode(AT); clr(); port->println(cmd); delay(100); return readStr(); } void HC05::parse(String s){ uint8_t i,l,pNum; String pName; String param[5]; l = s.length(); i=0; pNum=0; param[pNum]=""; pName=""; // имя команды while((s[i] != ':') and (s[i] != '=')){ if(s[i] != '+') pName = pName+=s[i]; i++; } i++; // параметры while((s[i] != 10) and (s[i] != 13)){ if(s[i] == ',') {pNum++; param[pNum]="";} else param[pNum] = param[pNum]+=s[i]; i++;} // запись данныхж if(pName == "NAME") name = param[0]; if(pName == "ADDR") address = param[0]; if(pName == "UART") {baud = param[0].toInt(); stopBit = param[1].toInt(); parity = param[2].toInt();} }Вы правы, ТАК не работает :(
Весь класс целиком. Рабочий.
Я уже перестал понимать к чему это. К #42 или к #44?
Но, в любом случае повторю второй раз (третий раз повторять не буду, просто буду игнорировать Ваши вопросы). Если Вы хотите задать вопрос, приводите скетч полностью, чтобы его можно было просто взять и запустить без дописывания и/или удаления чего-либо. Если будете приводить огрызки, ответов от меня больше не будет никаких.