Вот только “концы” искать гораздо проще именно в хорошо структурированном коде, чем в спагетти из кучи однотипных функций.
Небольшой туториал по ООП на ардуино - в данном случае компилировалось для ESP32, но вместо BluetoothSerial можно на моделях типа Uno использовать SoftwareSerial, итак:
void printDemo(Print &print)
{
print.print("Hello, world!\n");
}
void OopDemo()
{
BluetoothSerial btSerial;
printDemo(Serial);
printDemo(btSerial);
}
Есть теперь понимание почему в функцию printDemo можно запихать и обычный серийный порт Serial и BluetoothSerial/SoftwareSerial, а она просто будет работать?
для меня вот это “давайте загадочно подменять одно другим” выглядит как запутывание.
Конгресс, немцы какие то… Голова пухнет
Здесь нет по сути “подмены” - здесь просто и Serial и btSerial являются объектами совместимыми с “интерфейсом” Print и поэтому их можно использовать равноправно в алгоритмах работающих с этим интерфейсом одинаково и без оговорок.
Всё дело в том, что глобальная переменная Serial имеет тип HardwareSerial который наследуется от Stream который в свою очередь наследуется от Print.
Таким образом создатели платформы унифицировали и вывели в общую копилку комплекс методов типа print(String) так что с одной стороны разные классы их повторно используют, а с другой стороны мы можем объекты разных классов скармливать одним и тем же алгоритмам и они будут работать.
И это круто и сильно - это просто красота. Это ООП.
когда мне надо было один сериал поменять на другой я это сделал через замену названий и #define
Ну и дура.
вполне, вопрос только - каковы накладные расходы за эту красоту?
На микроконтроллерах этот правильный вопрос может быть значимым в узких случаях.
Как функция printDemo разбирается какой именно класс ей скормили чтобы вызывать разный код для разных классов?
Ответ таков: через таблицу виртуальных функций.
Virtual Table - далее сокращенно VTBL.
В C++ каждый объект замаранный в концепции ООП получает добавочное данное - указатель на таблицу виртуальных функций (полиморфных методов) и вызовы этих методов в машинном коде изменяются на более сложную конструкцию.
Предположим что у нас есть класс MyClass и в нём есть невиртуальный метод method1 и виртуальный (ООП-нутный) метод method2.
И вот код:
obj.method1(...);
obj.method2(...);
Как это дело разворачивается на глубинном уровне?
А вот так:
MyClass_method1(&obj, ... );
obj.vtbl->method2(&obj, ...);
То есть каждый объект участвующий в настоящем ООП хранит в себе указатель на таблицу где хранятся указатели на функции/процедуры его виртуальных методов - и вызов их всегда сопровождается дополнительной затратной вещью - извлечением из объекта по ссылке адреса косвенно процедуры куда передать управление.
Это приводит к тому, что вызов виртуальных функций в норме “нагружен” двумя дополнительными считываниями на уровне машинного кода по сравнению с вызовом невиртуального метода.
Это реальность.
P.S.
Может быть непонятно почему именно два дополнительных считывания - потому что сперва из объекта по некорому смещению считывается указатель на VTBL, а потом из VTBL считывается адрес куда передать управление (в машинном коде это как правило просто индекс в данной таблице).
Дедивана на вас нет!)))
Они - его потомки, чему ж удивляться ?
Сначала тоже так делал, а потом сделал через Print и успокоился ))
Плюсадин
Ой, да ладно вам! Давайте пихать ООП куда не попадя! Зачастую и смысла в этом никакого, а часто и самих плюсов нет. Ну да, в AVR есть, а в PIC, STM8, MCS51 нет и что теперь делать? Плюсадинить?
Во-во!
Ну нет. Вы должны объяснить свиньям, что они свиньи. Иначе всё очень просто.)
это защита от навязываемой информации
Ух ты! А можно поподробней? Патенты там и пр. интеллект.)
На досуге. Когда будет скучно. Интересно.
Ну как же ж! А как насчёт повода другим зацепиться за что нибудь? За какую либо оплошность? Ведь ничто так не радует, как неудача товарища, общеизвестно же!)
Причём, как ни странно, в интеллигентном обществе это особенно развито.(
Вот и я ровно про это же. Обычное дело ))