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

Stas633

Свой
  • Постов

    109
  • Зарегистрирован

  • Посещение

Сообщения, опубликованные Stas633


  1. On 7/17/2020 at 7:50 PM, Сергей Борщ said:

    Никуда оно не уплывет. Есть выравнивание по-умолчанию, есть принудительное. Предполагаю, что в каком-то из заголовочных файлов есть "незакрытая" #pragma pack и выравнивание/упаковка структур в других файлах зависит от того, был ли включен этот заголовочный файл перед другими в тот или иной исходник. Не был включен - будет по-умолчанию, был включен - будет принудительное в соответствии с указанной #pragma pack

    Спасибо, Сергей! "Заставили" проверить!

    Проект большой, "много-авторский" (4+), поэтому и не искал здесь... (" ...в каком-то из заголовочных файлов есть "незакрытая" #pragma pack...).

    Нашел, исправил (в ОДНОМ месте на 11Мб исходников, в строках подсчитать не возьмусь). Все норм. Вопрос снят! ))

    On 7/17/2020 at 9:19 PM, k155la3 said:

    ...

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

    ..

    Да, именно такой подход и использован. Т.е. формируется что-то типа FAT для внешнего хранилища (FLASH, EEPROM..), где в качестве параметров  в том числе и  размеры структур.

     

    On 7/17/2020 at 9:19 PM, k155la3 said:

    пока размер вложенности макроса не превысит возможный предел для компилятора. По крайней мере для IAR он был (возможно и есть, 256)

    Не встречался с таким.

  2. 14 hours ago, jcxz said:

    А почему пытаетесь врукопашную заменить компоновщик? Для размещения переменных в памяти ещё в прошлом веке придумали компоновщики. И в IAR "ессно" оно есть.

    Я бы с радостью! Но только, разве компоновщик умеет данные во внешней памяти размещать? (с которой обращение через слой spi/i2c)

    16 hours ago, SII said:

    Определить тип структуры в отдельном заголовке, причём явным образом указать для неё alignas (если используется Си++11 или старше; насчёт чистых сей не в курсе -- не использую в связи с бессмысленностью); тогда компилятор вольничать с выравниванием не будет.

    Да, такой выход рассматривался. Но, как самый простой и крайний (прямолинейный - "бери больше, кидай дальше...").

    Хочется же понять чем вызвано "поведение" компилятора и как этим "поведением" управлять? Действия компилятора скрытые (не очевидные), ни каких сообщений нет, а как итог - неработающий код. Да IAR честно предупреждает в "Development Guide": ".. A normal pointer can be implicitly converted to a pointer to  __packed , but the reverse conversion requires a cast....". Но "sizeof" - это ни разу не указатель. (

  3. В продолжение темы... Схожая ситуация...

    Определяю константы, конкретно  - таблицу адресов во внешней памяти (через #define ). При определении, ессно использую размеры размещаемых переменных (через sizeof()). В качестве переменных "участвуют" и структуры тоже. Вот кусочек..

    #define BEGIN_ROM 		0
    #define PRIMARYDATA_ROM	BEGIN_ROM + 10
    #define SECONDDATA_ROM	PRIMARYDATA_ROM+sizeof(struct MY_STRUCT)
      ....

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

    Спасает ситуацию только явный "запрет" упаковки  "#pragma pack()" перед объявлением структуры. Тогда размер структуры во врем проекте одинаков.

    Что это за "вольности" компилятора (линковщика скорее)? Или "лыжи", которые не едут, ни при чем?

  4. ... И всё это не в UART выводится а на 6 шаговых двигателя (стрелочки насажены). Ну там ШИМ программный....

    А что Вы в качестве драйверов ШД использовали? /если конечно это не "секрет фирмы"(с) :rolleyes: /

    Как я понимаю, должно быть дешевле 3x33970 или 6x293 с "обвесом"..

  5. .... Тогда буду разбираться почему у меня здоровый контрллер не входит в БСЛ при наличии валидной комбинации TCK и RST ...

    Плз., сообщите результат "разбора" и если возможно, схемотехнику.

    (около полугода назад попробовал FT232BM, "...чего-то там.." не срослось. Разбираться было некогда. Остался RS232....)

  6. И еще мнение, "..в догонку.."

     

    Во-первых, так ли уж необходимо производить опрос через флаг, устанавливаемый сис.таймером?

     

    .... намного проще опрашивать порт по таймеру. То есть, системный таймер выставляет флаг раз в 2-5 мс, по этому флагу отдельный поток считывает порт ....

    , разве не проще через Sleep(n) в самом процессе? /нет необходимости ни флаг создавать, ни конструкцию "сочинять" для установки флага с периодом, отличным от периода сис.тика (напр., флаг нужен через 3мс, а тик равен 1мс. А для Sleep(n) -> n=3 и "..все..")/.

    ...

    И если "останавливаться" на Sleep(n), то не проще ли опрашивать через время достаточное для устранения дребезга...

     

    Общепринятым считается:

    - опрос состоятия через ~2..10мк;

    - при получении изменения "задержка" опроса на ~50мс /тут два варианта или "пауза", или подсчет числа обращений (3мс х 17 раз = 51 мс). Для RTOS приемлем последний вариант, посностью согласен с MrYuran/ ;

    - повторный опрос;

    - принятие решения.

     

    А что если производить опрос через 50мс?

    Получается:

    - опрос /обнаружено изменение/;

    - задержка 50мс;

    - повторный опрос;

    - принятие решения.

    Дествия те же, но вот для RTOS от 17 (в нашем примере) циклов переключения процессов, со всеми вытекающими..., удается избавиться.

    ...

    Что теряем? - Увеличивается время реакции на нажатие с 3мс до 50мс. Но при скорости печатания 150 знаков/мин (это очень хороший результат, обычно 100...120) время между нажатиями 60/150 = 400мс. А предполижить, что кнопками прибора бутут пользоваться с такой скоростью затруднительно.. То есть, применительно к EmbeddedDevice, вероятность пропуска нажатия кнопки еще меньше.

    ....

     

    Быть может такое мнение ошибочно?

  7. Вы правы: отдельный процесс для обрабоки нажатия кнопок должен быть вне зависимости от способа реализации обработки; процесс этот низкоприоритетный и другим, более важным, процессам мешать не может (чего не скажешь о прерывании); да и "сложности", например, с выявлением длительного нажатия кнопок сводят на нет кажущуюся простоту INT-подхода.

    Павильное решение - периодический опрос состояния кнопок из процесса.

     

    Спасибо. :rolleyes:

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

    Подскажите как, с точки зрения RTOS, поступать правильнее :

    а) в прерывании от кнопок "сигналить" процессу, а уже в нем вводить паузу через Sleep(..), счивать скан-код, разрешать прерывания.

    б) паузу вводить в прерывании, там же получать скан, разрешать прерывания и, как писал в форуме dxp, отправлять процессу сообщение (а не флаг).

    ...

    Если "б)", то чем реализовывать паузу?

     

    недостатки способов относительно друг друга (на мой взгляд):

    для "а)" - сохранение контекста процесса обработки кнопок при "падении" в спячку (для реализации паузы); работа с прерыванием (разрешение прерывания) в процессе, а не в теле прерывания /"..баня, а через дорогу раздевалка.."/

    для "б)" - "длинное" прерывание.

  9. Почему? Тригонометрия реализуется и в фиксированной точке.

    Конечно, как и вычисление иттераций при реализации метода Ньютона.. :)

    Но в этом случае задача видоизменяется в задачу написания своей библиотеки. Стандартные библ. работают с плавающей точкой (возможно не все).

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

    Спасибо! :a14: Лежало на поверхности, но "глаза не видят" и "мозг не думает".

    P.S. бывают случаи, когда не подходит ни тот, ни другой формат. И тогда арифметика реализуется прямо в десятичном виде, с BCD представлением чисел и изменяемой длиной числа.

    Спасибо за идею! :)

     

    ....

     

    Попытаюсь "переписать" ответы:

     

    Главное - преобразования присутствуют везде. long - приведение к заданной точности, float - приведение к одной экспоненте и нормализация результата. При этом работа с целыми быстрее.

     

    "Итого:"

    Если применение "плавающей точки" не продиктовано использованием готового кода (библ.) То для написания более "быстрого" кода, необходимо использовать "фикс.точку". А для приведения целых чисел к заданной точности использовать умножение/деление не на 10^n, а 2^n.

    Если же скорость работы про-ца достаточна (или есть встроенный FPU), и точность вычислений обеспечивается разрядностью мантиссы, то - "плавающая точка".

     

    ....

     

    Спасибо. С Наступающим! Удачи! :)

  10. можно делить на 2 в степени n.

    Согласен. С точки зрения кода это, безусловно, и быстрее, и короче (чем умножение/деление на 10^n) - умножение и деление на 2 это, как известно, сдвиг, который длится 1 такт (с учетом разрядности МС конечно), но, тем не менее, это лишние, избыточные действия о которых НЕОБХОДИМО помнить при написании программы.

    А как быть если в различные моменты работы программы нужна разная точность? Передавать функции точность вычислений в качестве аргумента? Или писать несколько функций?

     

    Кесарю-кесарево. Математика (синусы, косинусы) вся на float? Гут - будем в плавучке. А управляющие алгоритмы редко выходят на плавучку - больше на long. Вот такой водораздел...

    Опять в "крайности"(сложности). Конечно, "тригономертию" без float ни как, но где та грань (водораздел, ватерлиния) когда "уже нужно"? Возведение в степень, деление..?

    ...

    Хорошо.. тогда так:

    допустим, необходимо вычислять такую фунцкию

    y=(x^2 + k) / z

    Вроде "МАТЕМАТИКИ" нет, но если не использовать float, то сразу возникает, как минимум, один вопрос: с какой точностью нужно производить вычисления?

    А если достаточная точность неизвестна перед началом программирования, да и вообще, может быть различной?

     

    ......

    ......

    Если попытаться ответить на вопрос топика, то правильно ли мнение, что:

     

    В случае, когда необходимая точность вычислений известна и не меняется, и математические формулы ограничены четырмя основными арифметическими действиями (+, -, *, /), то целесообразно применение long переменных, с соответствующими преобразованиями. В остальных случаях лучше, правильнее применять float.

  11. Все зависит от того, что вы собираетесь с вычисленными значениями делать. Да и что за задача (чисто вычислительная или управление чем-то).

    Не хотелось бы "углубляться", но... например.

    Один вариант - нужно расчитать значение и вывести его на индикатор. Выводить в формате плавающей точки ("1.234E-2"), согласитесь, не привычно для "глаза". Нужно точку "фиксировать" (0,01234).

    Другой вариант - нужно на расчитанное значение позиционировать, например, инструмент станка (руку робота и т.д.). И опять придется переводить float значение к единицам позиционирования (к ЦЕЛОМУ их числу). (А если дискретность перемещения не имеет 10-ное основание, то преобразование еще "длиннее").

    Т.е. на лицо необходимость "обратного" преобразования float -> long. /НO!, вместе с тем, функции, написанные с использованием float, проще для "понимания", переносимее, универсальнее, если хотите./

    Что может быть разным в этих вариантах? (с точки зрения работы с float)

     

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

    Каких именно?

    И еще одно уточнение... Вопрос достижения заданной точности не рассматривается. Хочется сравнить LONG v/s FLOAT /оверхед & скорость/

  12. Известно, что работа (выполнение арифметических операций) с целыми числами проще чем с дробными. Многие MC имеют в своем составе аппаратные умножители целых цисел. ("старшие" MC - DSP, ARM9 и др., имеющие мат.сопроцессоры, не рассматриваем).

     

    Изначально, все внутренние входные, "аппаратные" данные в MC (АЦП, таймер/счетчик) являются целыми. Но как только необходимо произвести операцию деления, то рассматриваемые числа "перестают" быть целыми, появляется целая и дробная, иногда равная 0, части.

     

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

    1. Увеличить разрядность чисел до необходимой точности (перед делением ужножить делимое и делитель, например, на 1000, тогда частное будет... (не продолжаю - очевидно) );

    2. Работать не с целыми (int, long), а с дробными (float) числами.

     

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

    ...

    P.S. Естественно, что вопрос не про собственно операцию деления, а про то, что перейдя к float, "придется" работать с float во всех остальных арифмет.действиях, использовать float в качестве аргументов и возвращаемых значений функций и т.д.

  13. Ув. bookevg, Вы, безусловно, во многом правы! :) (Мне кажется, что понятия виртуальный класс не существует вовсе. Абстрактный, т.е. не имеющий объектов/членов - да, такое понятие/механизм описан. Другое дело, если отнаследовать любой класс как virtual, для исключения создания более одной копии при косвенном наследовании, - пожалуйста. Да и к полиформизму наследование класса с идентификатором virtual отношения не имеет. Поправьте, если ошибаюсь.)

     

    Спасибо Вам за участие. :a14:

    Свой выбор "оптимального" способа я сделал. :) Правда, только благодаря помощи dxp. :)

    Не буду коверкать "первоисточник" - завтра вставлю цитатой... Смысл сказанного прост до банальности. :)

     

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

     

    Зачем из пушки стрелять по воробьям? Если, как в моем случае, из высокоуровнего класса вызывается функция низкоуровнего кл., при этом вызываемая функция просто знает/умеет дернуть ногой, вывести/принять данные по шине, и, при этом, ни как не "общается" ни с высокоуровневым классом, ни в низкоур-вом ничего не меняет, то зачем наследовать низ.ур в выс.ур? static и все! :) Я же, в желании "прочно" перейти к С++ программированию, считал использование static C-конструкцией, "недостойной" С++ программирования. :05:

    А вот если при вызове функций происходит взаимодействие с элементами классов, ну, например, несколько программных модулей выводят данные по одной и той же шине, и необходимо знать сколько байт данных передано кажным модулем, то без наследования тут никуда. Для каждого модуля должна быть своя копия счетчика, инкрементируемая вызываемой функцией... Ну и т.д. (Конечно этот пример можно "решить" и с использованием static, например добавляя к входным параметрам функции номер модуля... Но эффективность, понятность кода будет "никакой".)

    ....

    В общем, все как обычно - нужно делом, не важно каким, или заниматься всерьез, или не заниматься вообще. Нельзя изучить синтаксис С++ и заявить: "А я теперь умею программировать на С++!"

    ....

    Кстати, использование static вместо наследования сокращает программный код и время выполнения инициализации членов - отсутствуют "пустые" конструкторы, необходимые при наследовании... (и не только из-за этого :) )

     

    А если вернуться к вопросу выбора метода ("оптимального" - как я все время "просил" :) ) доступа к функциям класса, то в каждом конкретном случае он свой! В одном случае "хватает" static, а в другом необходима перегружаемая virtual функция.. :) Универсального решения не бывает!

     

    Еще раз спасибо.

  14. Я так понимаю различные классы будут обращаться к DBGU - т.о. надо сделать систему доступа к общему ресурсу или создать список заданий для DBGU

    Совершенно верно. Только ЧТО делать понятно, а вот что на счет того КАК делать?

     

    Мне кажется, что вы неправильно трактуете суть полиморфизма - советую прочитать Дж.Либерти - Освой С++ за 21 день. Попробую автора процитировать:

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

    Не соглашусь с Вами. В приведенной цитате речь идет о методах, т.е. виртуальных функциях . Которые естественно должны иметь свое "наполнение" в производных классах. Но я рассуждал не о наследовании виртуальных функций и полиформизме, а о наследовании virtual класса класса как virtual. Как я понимаю, виртуальный класс вовсе не "обязан" содержать виртуальные функции. Более того, виртульные функции можно создавать только в абстактнх классах. То есть объектов таких классов создано быть не может. Просто "не получится". Не так ли?

     

    А как по вашему работают cin и cout - ведь вы ведь нигде от них не наследуетесь, а они отвечают за ввод и вывод информации

    Извините, не понял к чему это... Я имел ввиду нецелесообразность обявления global объектов, если есть возможность сделать их локальными.

     

    Ничего не нарушается.

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

     

    До входа в main() все глобальные классы проинициализированы, т.е. вызваны все конструкторы.

    Так должно быть, но не всегда так есть. "Встроенный" Cstartup.s79 для IARARM НЕ СОДЕРЖИТ вызовов конструкторов до входа в main(). На форуме упомянуты способы введения такой инициализации (в частности, Сергеем Борщем).

     

    Вообще никогда friend-ом не пользуюсь

    А почему?

     

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

    Т.е. данными?

     

    Также можно создавать только static классы - что я очень часто и использую - на производительность это никак не сказывается, т.к. компилятор все это сводит к С-реализации

    Другими словами, если не смог (к Вам никакого отношения не имеет!) написать на С++ используй static и пиши как на С... :05:

    ...

    И все-таки, если класс не содержит ресурсов (переменных, данных), то будет ли формироваться избыточный код при его "многократном" наследовании?

  15. Поиск "оптимального" решения продолжается....

    Позволю себе напомнить "проблему"...

    Есть некий объект (класс), допустим, модуль отладки (DBGU) через UART. Естественно, объект может (и должен) инициализироваться, принимать и передавать инфу, и др.

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

     

    То, что использовать наследование DBGU для получения доступа к его свойсвам, не рационально описано выше в теме. Содание класса TDBGU как виртуального гарантирует, что при косвенном ("вложенном") наследовании "верхних" классов не будет создано двух копий TDBGU. А если virtual TDBGU наследуется двумя "отдельными" (разными, самостоятельными...) классами, то для каждого будет создана своя копия TDBGU... Или я не прав? И что вообще означает "производный класс содержит копию базового класса"? Про "создание" копий переменных базового класса при наследовании понятно, а как наследуются функции? Просмотр "откомпилированного" кода и здравый смысл подсказывает, что функции наследуются "беззатратно". Но если это так, то при условии остутсвия переменных в базовом классе его копия ничего не "весит"?

     

    Объявление глобального объекта mDBGU с использованием там "где нужно" конструкции extern ... mDBGU так же не лишено недостатков (как способ). Во-первых, глобальный объект "размещается" в "глобальной" области, при этом размещается "раз и навегда". Ну и, во-вторых, нарушается один из первейших принципов ООП - инкапсуляция. (Второе, конечно, относится скорее к стилю программирования нежели к "ошибкам", но если не выполнять правил, то зачем переходить на CPP?). Кстати, натолкнуло на эту мысль упорное "нежелание" IAR инициализировать глобальные объекты при startup'e.

     

    Далее... friend... ЧтО позволяет "делать" функция или класс , объявленные в другом как friend? Использовать функции (свойства) friend класса? НО! только через инициализированный объект mDBGU! А как быть если этот объект еще не "создан" или не "виден"?.... (И вообще, насколько я понял, конструкция friend "нужна" для доступа к privat переменным friend класса.)

     

    Остается доступ к свойствам (функциям) DBGU через их объявление с квалиф. static. Ранее я писал, что "...static гарантирует, что будет создана только одна копия этого элемента...". Это справедливо, но только для переменных (может я ошибаюсь?). Для функций же это квалификатор "устанавливает" глобальную "видимость" и "доступность" даже до инициализации объекта mDBGU..и только.

     

    Таким образом, объявив ф-цию как в staticах, можно ее "использовать" где угодно, конечно предварительно "показав" ее (класс) через #include. Это очень "смахивает" на Си-подход, но лучшего в голову не приходит... Хотя спинной мозг "подсказывает", что не все так однозначно... :)

     

    Просвятите, плз.

  16. ...

    Спасибо. Согласен... Наследование для решения вопроса доступа - это, как минимум, не экономно.

    ...

    Если говорить по теме, то вопрос доступа решил так, как рекомендовал dxp.

    А именно:

    - каждый объект "размещен" в своей "единице компиляции" (ЕдКомп) /связка файлов, например, One.h и One.cpp/ (так делал и раньше);

    - член класса /TOne mTOne;/ объявляется теперь в TOne.cpp, а не в main.cpp как раньше;

    - а для возможности использования функций (свойств) TOne, там где нужно, mTOne объявляется с квалификатором extern.

     

    ... файл One.h

    ...
    class TOne
    {
    ...
    public:
    void FuncOne(void);
    ...
    }

    ... файл One.cpp

    ...
    TOne mTOne;
    ....
    void FuncOne(void)
    {
    }

    ... файл Two.h

    ...
    class TTwo
    {
    ...
    public:
    void FuncTwo(void);
    ...
    }

    ... файл Two.cpp

    ....
    #include "One.h"
    
    TTwo mTTwo;
    extern TOne mTOne;
    ....
    void FuncTwo(void)
    {
    ...
    mTOne.FuncOne();
    ...
    }

    На мой взгляд, наиболее простой и понятный способ.

     

    А если говорить не совсем по теме, то главная проблема ("разруха" в первоисточнике) "...в головах, а не в клозетах..." (с) Нельзя заниматься ООП оперируя процедурами. Как только приходишь к пониманию "объекта", так все очень упрощается. И "создание" (представление) модели программы, и понимание взаимоотношений между разными частями этой программы. Не буду пересказывать написанное Гради Бучем (и про мыслительный процесс человека в повседневной жизни, и про количество абстракций, с которыми человек способен работать одновременно, и т.д.), но, на мой взгляд, достаточно прочесть главу "1.2. Структура сложных систем" и вопросов о полезности и естественности перехода к ООП не остается. Мир вокруг нас состоит из Объектов, а не из Процедур. И живем мы, используя Объекты и их какие-то свойсва (Процедуры), а не Процедуры каких-то Объектов.

  17. Словом, вам надо определиться, какие у вас отношния между объектами, и строить код уже исходя из этого. А на ощупь и методом тыка ничего хорошего не получится.

    :)

    Спасибо. Ассоциативное объяснение самое доходчивое, а главное "заставляет" мыслить "объектами"... хоть "слонами", хоть "яблоками". :)

    "Обложился" книгами.. Учу "зоологию". :)

  18. Чтобы не было "гаданий на кофейной гуще" опишу коротко "историю" возникновения вопроса... Хоть с темой это и не связано.

    - была рабочая программа на Си. Программа содержала готовые "блоки" для работы с LCD, интерфейсы I2C, SPI, UART, работа с кнопками, энкодером и т.д;

    - при "проектировании" нового изделия было принято решение о переходе на С++ (причины принятия решения опускаю);

    - мне показалось достаточным "обозвать" (объявить) блоки классами. Для доступности функций интерфейса (LCD и т.д.) и проводилось прямое, читай - бездумное, наследование.

    - пока объема озу (стека) хватало для размещения всех копий базовых классов все было "спокойно"... А как только порог был превышен, так сразу и появился, вопрос, вынесенный в топик. Поэтому подтекст вопроса примерно такой: "Как можно обратиться к функции, объявленной в другом классе (члену другото класса - так правильнее :) ), без "потери" памяти на это обращение". Но в таком виде писать вопрос в TOP стыдно .. (видимо напрасно стыжусь - учиться никогда не поздно).

     

    И friend и static весьма "полезны": friend позволяет получить доступ без создания копий (т.е. буквально то, что я хотел), а обявление элемента с квалификатором static гарантирует, что будет создана только одна копия этого элемента. (Пишу очевидные вещи для таких как я начинающих С++).

     

    Еще раз спасибо.

     

    "...Чем лучше программист знает С, тем труднее будет для него при программировании на С++ отойти от

    стиля программирования на С...." (с) Бьерн Страуструп.

     

    ....

    А чем наследование лучше/хуже доступа через friend? (для рассматриваемого случая)

  19. Или вам что-то другое надо?

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

    В предложенном варианте необходимо "помнить" (держать в памяти) где и с каким именем объявлен объект slon. Если он объявлен в main - файле, то в в файле описания TMamont нужно этот обект объявлять как extern и т.д. Т.е. опять "возврат" к Си.

    :bb-offtopic:

    По "точный" ответ на "точный" вопрос вспомнился старый анекдот... Когда воздухоплаватель на воздушном шаре приземлился в незнакомой местности и спросил у проходившего мимо программиста: "Где я нахожусь?". И тот, после недолгого раздумья, ответил: "Вы находитесь в корзине воздушного шара." Ответ правильный и точный но... :)

    (не примите, плз., это на свой счет, это так, к слову).

    ...

    Правильнее, на мой "просветленный" :) взгляд, построить "объектную модель программы" и создать иерархию классов с использованием абстактных классов (гл.12, "Язык программирования C++", Б.Страуструп). Хотя для Си - программиста это и покажется более сложным. Кстати, предложенную Вами реализацию меню через наследование абстактного класса успешно реализовал, правда, как оказалось, не понимая сути :)

     

     

    Тут надо смотреть, как правильнее по смыслу.

    ...

    Хорошо об этих проблемах написано в книге ""Философия Java".

    Вы правы - "Философия...". :) Т.е. нужно менять подход, а не искать "средства".

    В общем-то, вопрос о способах доступа раскрыт, как мне кажется, полностью.

    Главный вывод это то, что нельзя (неправильно, трудно, неэффективно...) использовать объекты для описания процедур.

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

    Спасибо!

     

    P.S. Способов два:

    - через наследование. НО! Если базовые классы не являются абстрактными, но наследование приводит к избыточному наличию копий базовых классов (обектов) (если не прав - поправьте);

    - через указатель на класс (объект). НО! Здесь проявляется вся "прелесть" продердурного программирования и смысл перехода от Си к С++ теряется.

    ... предложенное dxp "включить один (класс) в другой" не рассматриваю, так как производных классов несколько, и включение базового в каждый .............

     

    Вместе с тем, любой из вариантов работоспособен и не является ошибочным. И, в зависимости от ситуации, может быть применен.

  20. :bb-offtopic:

    Ув. dxp, спасибо за Ваши советы/рекомендации (в другой ветке)!!!

    ... При внешней схожести С и С++ очень разные языки... ...разные тем, что на них думать надо совсем по-разному... ...

    Попробуйте думать над программой на уровне объектов реального мира, ..., тогда реализация ляжет на классы почти сама собой... (с) dxp

     

    Начал ознакомление с работами Страуструпа и Буча ("настольной книгой", до последнего времени, был "Самоучитель..." Шилдта) и сразу понял, что, используя синтаксис и правила С++, пишу на Си! Отсюда и "проблемы" вида: ".. как сделать функцию доступной.." и т.д.

    При написании программы я использовал "прямое" наследование для получения доступа к функциям базовых классов. Т.е. при создании объекта производного класса создавался объект/копия базового класса и таких копий было "множество", хотя достаточно одной единственной. Последствия, в виде "нехватки" ОЗУ, очевидны... Отсюда и мой интерес к получению "беззатратного" доступа к "чужим" функциям.

    Программу нужно писать на С++, а не на Си, изпользуя С++ синтатсис и правила!! :) Т.е. начинать нужно с рисования структуры связей и т.д.

    Спасибо еще раз! :a14:

     

    ... Если классы живут сами по себе, то ничего вышесказанного делать не нужно, а общение меж ними организовать на открытой части (public)....

    Что Вы имеете ввиду? Если не через наследование, то как тогда?

  21. Допустим:

    - имеются два класса TOne и TTwo;

    - в TOne определена ф-ция FuncOne();

    - классы определены в разных единицах компиляции (файлах);

    - созданы объекты (глобальные) этих классов (mOne и mTwo) в третьем файле - "main.cpp".

    Вопрос:

    Как из класса TTwo получить доступ (использовать) функцию FuncOne() класса TOne?

     

    1. наследовать TOne в TTwo

    2. перед описанием класса TTwo объявить объект классаа TOne: extern TOne mOne.

    3. ...?

    ...

    А если таких "вложений" больше? (Т.е. функции TTwo, использующие TOne, необходимы в TThree)

     

    Что лучше? Как правильнее? Есть ли разница в испозовании стека?

     

    Прим. Например: TOne - класс описывающий работу с индикатором (LCD), а TTwo - класс формирования данных для вывода на индикатор. Рассматривается ситуация одного процесса (не RTOS).

     

    Прим.1 Вопрос задаю здесь, так как обсуждение темы о выделении раздела форума для "чистого" программирования, увы, ни к чему не привело. :) Господа Админы/Модераторы извините, просмотрел..

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