Перейти к содержанию
    

Как писать на С++ при создание приложений под ARM

Два, нет - три вопроса:

  1. Какое отношение имеет ООП к динамической памяти?
  2. Почему вы ставите знак равенства между С++ и ООП?
  3. Причём тут вообще паттерны проектирования? Какое они имеют отношение к ЯП С++?

 

Начну со второго: да, возможно я излишне строго приравнял C++ и ООП. Это конечно не так, я немного не то имел ввиду. Конечно, C++ дает нам много вкусностей помимо "классического" ООП. Но, согласитесь, основные достоинства языка С++ заключаются в его возможности писать ОО-код. Именно поэтому я не приравнял С++ и ООП, но поставил их совсем рядом и позволил себе в первом посте сместить акцент в сторону ООП.

Отношение ООП к динамике? Да никакого! Но во многих учебниках, а также в ВУЗах, преподается ООП (позвольте я все же буду тут писать про ООП, коль уж мой первый пост был де-факто про него) в тесной свзяке с динамической памятью, ибо так легче показать виртуализацию и полиморфизм. Поэтому и написал, что "ООП пытается тянуть за собой ....". Согласитесь, многие вещи удобнее реализовываются если использовать динамику? Однако этого лучше не делать, либо делать с умом, понимая механизмы и возможные последствия.

Паттерны? А разве плохо знать о них? А к С++ они имеют прямое отношение: их можно реализовать используя С++, а вот на С сделать то же самое гораздо сложнее.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Да, стоит сделать new, как c++ тянет библиотечный malloc и еще иногда кучу всякого несовместимого. а если у вас защищенная ось, у каждого треда свой stack,data,heap, то и malloc должен быть свой(если есть, у меня нету heap пока,не нужно пока), и попробуй ему обясни куда надо класть данные. и еще многие вкусности c++ тянут...мотому я пишу C.

Хотя для графики(всяких там менюшек) лучше(удобнее) c++, мож буду юзать, когда нужно будет сложную графическую оболочку делать

а ООП и на C кодится, (какая разница, написать obj.func(x) или чтото типа class_func(&obj,x) ?), хоть и далеко ему до наворотов C++

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вынужден категорически не согласиться по всем пунктам.

 

Начну со второго: да, возможно я излишне строго приравнял C++ и ООП. Это конечно не так, я немного не то имел ввиду. Конечно, C++ дает нам много вкусностей помимо "классического" ООП. Но, согласитесь, основные достоинства языка С++ заключаются в его возможности писать ОО-код. Именно поэтому я не приравнял С++ и ООП, но поставил их совсем рядом и позволил себе в первом посте сместить акцент в сторону ООП.

Давайте наведём ясность с терминами, в частности с ООП. ООП - объектно-ориентированное программирование, в основе которого лежит описание программы в виде иерархий наследуемых объектов с виртуальными функциями (кои являются методами), с возможностью переопределять поведение методов на любом уровне иерархии. Т.е. если я просто определяю некий класс со своим представлением (данными) и интерфейсом (открытыми функциями-членами), объявляю объект[ы] этого класса и использую его[их] в программе - то это получается не ООП. Тут нет ни иерархий, ни наследования, ни методов (виртуальных функций). И даже если было бы и наследование, но без виртуальных функций, то и это было бы не ООП - т.к. ключевым свойством ООП является динамический полиморфизм - т.е. подмена поведения метода в наследнике на рантайме, а само по себе наследование этого не даёт.

 

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

  1. Процедурую, когда программист разбивает код на функции (процедуры) и строит программу как совокупность вызовов процедур (точно как в традиционном С).
  2. Объектную, когда программист размещает код в компактных объектах, скрывая детали реализации с помощью инкапсуляции и абстракции и определяя интерфейс для взаимодействия объектов с внешним (для объекта) миром, а программу, строит определяя взаимодействия объектов между собой и с другими программными сущностями.
  3. Объектно-ориентированную, когда код строится в виде иерархий полиморфных классов (см выше).

Каждая парадигма имеет свои сильные и слабые стороны, и применять их все три надо к месту. С++ все эти три парадигмы поддерживает одинаково хорошо. И ни одна из них не порождает сколько-нибудь заметных накладных расходов в программе, чтобы их нельзя было использовать даже на 8-битных малышах, не говоря уже об АРМах. Где уместен С, там уместен и С++.

 

Отношение ООП к динамике? Да никакого! Но во многих учебниках, а также в ВУЗах, преподается ООП (позвольте я все же буду тут писать про ООП, коль уж мой первый пост был де-факто про него) в тесной свзяке с динамической памятью, ибо так легче показать виртуализацию и полиморфизм.

Совершенно нет. Никакого абсолютно отношения виртуальные функции и полиморфизм не имеют к работе со свободной памятью. Это полностью ортогональные вещи. Механизм виртуальных функций в С++ строится на таблицах указателей на функции - это вполне обычный механизм, хорошо известный ещё из языка С. Разница в том, что в С эти таблицы создавать, инициализировать адресами функций приходится вручную, в С++ этим занимается компилятор. Когда вы пишете код с таблицами указателей на функции на С, вам же не приходит в голову их размещать, в обязательном порядке вызывая malloc() - эти таблицы вполне нормально живут в статической памяти (да хоть в стеке, если время жизни таблицы достаточно для обслуживания кода, её использующего).

 

И если я описываю иерархию классов и создаю объекты статически, то и никакой динамической памяти и близко не возникает, а полиморфизм присутствует в полный рост. И ситуация тут наглядная и простая - уж во всяком случае попроще (не сложнее точно), чем в случае размещения объектов в свободной памяти

 

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

Не соглашусь. Использование динамической (свободной) памяти обусловлено только одним обстоятельством - когда на этапе написания программы неизвестно, сколько потребуется тех или иных объектов, когда достоверная информация об этом появляется только на этапе выполнения. Сама по себе работа со свободной памятью - это более сложный и ответственный процесс, ошибки в котором почти всегда приводят к катастрофическому падению программы (удаление несуществующего указателя, например) или в лучшем случае к утечке памяти. И накладные расходы на создание/удаление объектов в свободной памяти ни в какое сравнение не идут с аналогичным процессом в статической памяти или стеке. В любом случае работа с динамическими объектами требует повышенной внимательности, аккуратности и ответственности, а накладные заметно выше. Поэтому применять это надо только там, где это действительно необходимо, и не более того.

 

В программах для РС, где присутствует GUI, это обычная практика - создавать объекты с помощью оператора new, т.к. GUI сам по себе динамичен - окна появляются и закрываются, действия пользователя на этапе написания программы не известны. Учитывая, что операция создания объекта в свободной памяти на РС не очень дорогая, этот подход широко применяется там и для других потребностей. Но и там злоупотреблять этим ни к чему. И недаром тов. Александреску даже разработал специальный быстрый менеджер памяти для использования его с небольшими объектами (он подробно описан в его книжке "Modern C++ design").

 

Паттерны? А разве плохо знать о них? А к С++ они имеют прямое отношение: их можно реализовать используя С++, а вот на С сделать то же самое гораздо сложнее.

Знать о них не плохо. Только это, вообще-то, приёмы проектирования, и реализовываться они могут на многих языках, а не только на С++. И уж нет никаких особы предпосылок к тому, чтобы считать их непригодными для реализации в embedded системах, в частности на ARM. Например, паттерн Singleton очень хорошо реализуется не то, что на ARM, но даже на AVR, и мы давно и широко его применяем.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

  1. Процедурую, когда программист разбивает код на функции (процедуры) и строит программу как совокупность вызовов процедур (точно как в традиционном С).
  2. Объектную, когда программист размещает код в компактных объектах, скрывая детали реализации с помощью инкапсуляции и абстракции и определяя интерфейс для взаимодействия объектов с внешним (для объекта) миром, а программу, строит определяя взаимодействия объектов между собой и с другими программными сущностями.
  3. Объектно-ориентированную, когда код строится в виде иерархий полиморфных классов (см выше).

Спасибо за обзор. Лично для меня он оказался весьма полезным и своевременным.

Оказывается, я обычно реализую подход №2 ("объектный"), средствами языка Си: разделение интерфейса и реализации - module.h и module.c, сокрытие данных и служебных функций - ключевое слово static.

В связи с этим вопрос: не могли бы Вы привести примеры микроконтроллерных задач, в которых "объектно-ориентированный" подход даёт ощутимые преимущества?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

 

В связи с этим вопрос: не могли бы Вы привести примеры микроконтроллерных задач, в которых "объектно-ориентированный" подход даёт ощутимые преимущества?

пример - графический юзверь-интерфейс. я какраз щас задачку ставить буду, где сложный gui, все на том же cortex-m3, QT там не пойдет, прийдется писать свой, тут и применю с++, посему и залез в эти темку :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

пример - графический юзверь-интерфейс. я какраз щас задачку ставить буду, где сложный gui, все на том же cortex-m3, QT там не пойдет, прийдется писать свой, тут и применю с++, посему и залез в эти темку :)

При всём уважении, это голословное утверждение. Делал GUI (насколько сложный - тут мнения в любом случае разойдутся) именно "объектным" способом, никакой тяги к "полиморфизму" не ощутил.

Отсюда и вопрос. Хотелось бы с обоснованием, где именно полиморфизм реально помогает.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

...где именно полиморфизм реально помогает.

Из практики: измерительная система, реализующая несколько различных измерений параметров одного объекта. Количество и последовательность измерений определяются пользователем. Оказалось удобным сделать классы конкретных измерений производными от общего базового виртуального класса. Структура программы получилась независимой от деталей (и изменений) каждой конкретной технологии.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

При всём уважении, это голословное утверждение. Делал GUI (насколько сложный - тут мнения в любом случае разойдутся) именно "объектным" способом, никакой тяги к "полиморфизму" не ощутил.

тут не полиморфизм, тут иерархия классов, а это уже ооп, а не оп. хотя и полиморфизм хорошо подходит.

покрасить кнопочку в синий цвет и покрасить окошко в синий цвет - совершенно разные процедуры, хотя их можно обьеденить в один метод "покрасить виджет"

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

При всём уважении, это голословное утверждение. Делал GUI (насколько сложный - тут мнения в любом случае разойдутся) именно "объектным" способом, никакой тяги к "полиморфизму" не ощутил.

Отсюда и вопрос. Хотелось бы с обоснованием, где именно полиморфизм реально помогает.

Это вы зря - GUI, как раз, то место, где ООП рулит в полный рост. Не знаю, как у вас было организовано, но типовая схема такая: все объекты GUI являются членами иерархии классов, где в базовых классах объявляются методы (т.е. виртуальные функции), назначение которых выполнять действия, сходные по назначению, но разные по реализации. Например, у нас есть объекты: главное меню, выпадающее/всплывающее меню, пункт меню, графическая кнопка, строка состояния и т.п., а так же есть органы управления прибором - несколько кнопок или небольшая [плёночная] клавиатура. Нам нужно организовать управление всем ворохом графических объектов, но сделать это как-то единообразно. К примеру, нужно обрабатывать кнопку Right (перемещение вправо). Для этого мы в базовом классе иерархии объявляем виртуальную (если в самом базовом, то кошерно её объявлять чисто виртуальной - pure virtual) функцю void right(). И во всех производных классах, где это необходимо, эта функция переопределяется, чтобы получить для каждого класса своё поведение.

 

Например, при нажатии на эту кнопку, если фокус находится на главном меню, то курсор перемещается на следующий пункт главного меню. А если фокус находится на пункте выпадающего меню, то если пункт меню указывает на подменю, то открывается это подменю, в противном случае ничего не делается. А если фокус на строке состояния, то, скажем, это приводит к перемещению элементов строки состояния по кругу - самый правый уходит в самый левый, а остальные смещаются на одну позицию вправо. Словом, тут как требования задачи лягут и как фантазия позволит.

 

Так вот, код для реализации этого будет до безобразия прост:

    TWidget *focus; // указатель на активный графический элемент (виджет)

   ... 

    focus = ...;   //  focus присваивается адрес графического элемента - как правило это присваивание происходит 
                      // внутри методов самих виджетов 

   ... 

    if( is_key_clicked(GUI::KEY_RIGHT)
    {
        focus->right();
    }

И всё. Код, разделённый ... находится в разных местах программы. В частности, внутри функции right() фокус может получить другой адрес, чтобы следующие нажатия на кнопки управления обрабатывались уже другими объектами.

 

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

 

Другой пример, как уже сказали, - коммуникации. Я делал так приём пакетов по каналу управления: приходит пакет данных, надо его спровадить обработчику. Удобно обработчики выполнить в виде классов, у которых есть виртуальная функция, которая собсно и обрабатывает пришедшие данные. В самом входящем пакете в заголовке есть код, обозначающий тип пакета, заводим массив объектов классов, производных от одного базового абстрактного класса, заводим массив указателей на объекты-обработчики, инициализируем его адресами этих объектов (тут нужно проявить аккуратность). При приёме входящего пакета в обработчике прерываний декодируется заголовок и делается извлечение адреса из массива указателей и вызов метода обработчика.

 

Это сильно похоже на аналогичный прием на С, когда заводится массив указателей на функции, но в данном случае есть удобство том, что объект-обработчик инкапсулирует в себе все необходимые сущности для работы. Кроме того, у объекта можно завести не один, а несколько методов, т.е. расширить это дело.

 

В общем, в любом приложении, где есть общие по назначению, но разные по исполнению действия, уместно применять ООП - динамический полиморфизм отлично ложится на реализацию такой задачи.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

dxp, как у вас терпения хватает писать такие длинные посты? ;) Думаю, это будет отличным стимулом и введение в ООП, многих эмбедеров форума. Недаром говорят, «Новосибирск - город математиков и программистов»

Во общем респект и уважуха!!!

 

P.S. может в контексте этой темы и ваших постов Страуструпа выложить?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В общем, в любом приложении, где есть общие по назначению, но разные по исполнению действия, уместно применять ООП - динамический полиморфизм отлично ложится на реализацию такой задачи.

Другими словами, средства полиморфизма из Си++ могут быть оправданы там, где на Си нужно было бы вводить "поле типа" или массив указателей на функции.

Неудивительно, что у меня нет к этому тяги: в моих задачах это редко встречается :-)

Ещё раз, спасибо за развёрнутое объяснение.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Другими словами, средства полиморфизма из Си++ могут быть оправданы там, где на Си нужно было бы вводить "поле типа" или массив указателей на функции.

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

dxp, спасибо большое за обзорчик, вроде все и так понятно, но очень удобно, когда все собрано в кучке. распечатаю себе, как шпаргалку/краткое пособие ;)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

dxp, спасибо за шикарные ответы!!! :disco:

Не моголи бы Вы подсказать, по какой литературе обучались? Или это опыт?

Спасибо!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Расказ про ООП конечно увлекательный, но стоит учитывать, что как только вы вылезете за область применения, описанную dxp (наследование от интерфейса и поточная обработка), то тут же вся система станет крайне неустойчивой. Конкретно: множественное наследование сразу ставит крест на проекте, перегрузка функций и операторов приводит к очень хитрым багам, развесистая иерархия наследования приводит к хрупкости системы - очень высокая связность элементов... Использование паттернов не имеет отношения к С++, но в микроконтроллерах не имеет особого смысла. Куда можно в AVR засунуть синглтон?! Использование шаблонов С++ сильно тормозит компиляцию и плохо контролируется по расходу памяти.

В общем, это неправда, что С++ оправдан везде, где оправдан С.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...