ООП против Inline с Arduino

8

Я давно программирую, но я новичок в программировании Arduino и AVR. Главный вопрос, который у меня возникает по поводу программирования этих микроконтроллеров, заключается в том, есть ли серьезные различия в разработке кода в объектно-ориентированных классах по сравнению с более традиционным встроенным программированием, которое я видел во многих примерах?

Другими словами, в мире контроллеров Arduino / AVR есть ли экономия памяти и производительности за счет использования классов или наоборот?

Скажем, например, у нас есть класс:

class SomeClass(){

private:
   int x;
   int y;

public:
   void foo();
   void bar();
}

SomeClass thisClass;
thisClass.foo();
thisClass.bar();

Будет ли какая-либо производительность или увеличение памяти при разработке программы более встроенным образом, например:

int x;
int y;

void foo(){ /*** Do something ***/};
void bar(){ /*** Do more stuff ***/};

Я попытался выполнить некоторые операции поиска в Stack Exchange и Google, но не смог найти ответ, который я искал, и самым близким, что мне удалось найти, был вопрос об обмене стека.

Причина, по которой я спрашиваю об этом, заключается в том, что у меня есть проект, который должен быть как можно более легким, и я не понимаю, как мне следует разрабатывать свою программу в этой среде.


редактировать

Спасибо за ответы, это пролило свет на вещи. Есть одна вещь, которая мне не совсем понятна.

Скажем, у вас есть проект, который вы разрабатываете, который использует u8glib следующим образом:

class UserInterface{
private:
   U8GLIB_ST7920_128X64 Display;

public:
   UserInterface();
}

Как бы вы обошлись, используя «Динамическую память», как:

UserInterface::UserInterface(){
   UserInterface::Display = U8GLIB_ST7920_128X64(LCD_E_PIN, LCD_RW_PIN, LCD_RS_PIN, U8G_PIN_NONE);
}
Энди Брахам
источник

Ответы:

2

Главный вопрос, который у меня возникает по поводу программирования этих микроконтроллеров, заключается в том, есть ли серьезные различия в разработке кода в объектно-ориентированных классах по сравнению с более традиционным встроенным программированием, которое я видел во многих примерах?

...

Другими словами, в мире контроллеров Arduino / AVR есть ли экономия памяти и производительности за счет использования классов или наоборот?

Да, существует большая разница между использованием C или C ++ для небольших встроенных систем, таких как Arduino / AVR. C ++ позволяет предоставлять больше информации для оптимизации компилятора.

Если вы реализуете структуру ООП, платформа C ++ или среда выполнения также могут помочь с архитектурой программного обеспечения и его повторным использованием. В Cosa используется несколько шаблонов проектирования ООП для создания интерфейсов как для программистов приложений, так и для программистов драйверов устройств. Наиболее распространенным является делегирование .

Использование абстрактных классов, виртуальных функций-членов, встраивания и шаблонов может помочь добиться более низкого отпечатка и более высокой производительности, чем традиционные реализации. Например, классы Cosa Pin X5-X10 быстрее, чем ядро ​​Arduino, и в то же время меньше по размеру. Пожалуйста, смотрите тесты .

Одна вещь, которую нужно «отучить» от традиционного программирования на C ++ - это использование new / delete (malloc / free). При размере SRAM всего в несколько килобайт использование кучи представляет большой риск. Ответ - статические классы и данные на основе стека.

Об архитектуре инфраструктуры ООП можно сказать гораздо больше, но я надеюсь, что это поможет ответить на ваши первоначальные вопросы.

Ура!

Микаэль Патель
источник
Хороший ответ! Я обновил свой вопрос, спрашивая о том, как обойти динамическую память (new / delete / malloc / free). Есть ли у вас какие-либо данные о неиспользовании динамического распределения памяти? Должно ли все, что должно быть распространено среди классов, быть глобальным? Это звучит неправильно для меня, меня всегда учили не использовать глобалы, если вы можете помочь.
Энди Брахам
Краткий комментарий к вашему примеру UserInterface выше. Отображение на самом деле является составом объекта (не ссылкой / указателем), поэтому вам не нужно новое. Вам нужно запустить дисплей. Конструкция UserInterface должна выглядеть примерно так. UserInterface::UserInterface() : Display(LCD_E_PIN, LCD_RW_PIN, LCD_RS_PIN, U8G_PIN_NONE) { ... }, Необходимые параметры конструктора Display должны быть переданы в конструктор UserInterface.
Микаэль Патель
ООП - это инкапсуляция, скрытие данных, поэтому обмен должен быть минимальным (ноль). Цель состоит в том, чтобы более или менее иметь только несколько глобальных статических объектов, которые взаимодействуют. Они держат и скрывают глобальное состояние. Для объектов данные-члены являются нединамическим локальным состоянием. Для достижения цели вам снова понадобится множество хитростей; программные преобразования.
Микаэль Патель
Пример; Рассмотрим конструкцию класса BitSet, который может иметь переменное число членов. Очевидное решение состоит в том, чтобы вычислить количество требуемых байтов и использовать new / malloc. Альтернативой является передача хранилища в качестве параметра конструктору BitSet. Третий вариант - это шаблонный класс с количеством элементов в качестве параметра. Это позволяет использовать переменную-член с необходимым количеством байтов. Пожалуйста, смотрите Cosa / BitSet.hh для более подробной информации об этом варианте преобразования программы. Есть еще трансформации.
Микаэль Патель
Я обновил свой пример UserInterface, это более или менее правильный подход? Я думаю, что теперь у меня есть очень хорошее понимание того, как реализовать то, что мне нужно.
Энди Брэхем
4

Причина, по которой вы не можете найти ответ, заключается в том, что ответом является как Да, так и Нет.

Что касается базового класса - определения вашего класса с помощью методов и т. Д. И создания объектов из него - разница в конечном результате невелика по сравнению с «ванильным» C. Оптимизации компилятора теперь настолько хороши, что производительность остается той же. Да, может быть небольшое увеличение использования памяти, так как вы передаете дополнительный указатель при каждом вызове метода (вместо foo(int x)вас foo(MyClass *this, int x)), но это настолько мало, что не будет заметным.

Большие различия возникают, когда вы начинаете играть с полиморфизмом и другими сложными темами. При запуске этих сложных программ компилятор не всегда может определить, какие функции требуются, а какие нет, и он не может вырезать неиспользуемые функции ( «сборка мусора» ). Таким образом, вы можете в конечном итоге получить больший код.

Это не означает, что код медленнее, просто куски кода, которые торчат вокруг, никогда ничего не делают.

Более важным является управление вашей динамической памятью лучше, чем вы привыкли. Поскольку памяти так мало, куча очень мала, и в результате она очень легко фрагментируется. Динамическое создание и уничтожение объектов ( new myClass, delete myClassObject, и т.д.) очень плохо. Объекты класса действительно должны быть статически определены (в глобальной области видимости наиболее распространены) или временно размещены в стеке (локальные экземпляры). В противном случае вы напрашиваетесь на неприятности - и первое, о чем вы узнаете, это странные вещи (никаких сообщений об ошибках или исключений, видите ли ...).

Маженко
источник
Хорошо, если я правильно понимаю в этом мире программирования, классы используются для большей организационной роли и переносимости, чем для истинного ООП, и что вещи должны быть распределены статически и явно, а не динамически. Большое спасибо, это имеет большой смысл и объясняет, почему я видел примеры и что не написано так, как они есть.
Энди Брахам