помогите с динамическим созданием массива
- Войдите на сайт для отправки комментариев
Втр, 07/06/2016 - 15:04
Cуть хотелки - в объекте создать массив из элементов "структура" кратно одному параметру конструктора.
Вроде оно то, но что-то меня смущает, да и компилятор ругается на последнюю точку (строка 18)
typedef struct { long ValueTec; // текущее выведеное значение на экран char Mask; // маска символов uint8_t FirstCol; // номер колонки первого символа uint8_t EndCol; // номер колонки последнего символа boolean Active; // номер колонки последнего символа } OutParamText; class Lcd_I2C_4x20: public LiquidCrystal_I2C { private: OutParamText *param; public: Lcd_I2C_4x20(uint8_t CountParam) : LiquidCrystal_I2C(0x27, 20, 4){ param = (OutParamText*) malloc(CountParam*sizeof(OutParamText)); for (uint8_t i = 0; i < CountParam; i++){ (OutParamText)(¶m + CountParam*sizeof(OutParamText)).Active = false; } }
еще боюсь со звездочками чего напутал :) а это будет сложно определить потом...
замени на:
Кроме того, у тебя там ошибка и не одна, навскидку около 3-х ошибок, хотя смотря как считать.
По сути Object *array_ptr и Object array[] - это близкие вещи, хоть это и не совсем одно и тоже, но это не важно.
И еще, почитай уже о массивах, там всё достаточно просто и понятно, когда почитаешь и попробуешь на кошках, а не на мульти проектах. Массив структур - это вообще песня, удобно и просто, когда нужно.
Совет, если не хочешь, не читай. Не пытайся пользоваться приведением типов, забудь, что оно есть, сначала пойми, для чего нужно приведение типов, а потом сам поймешь, что в 95% случаев оно нафиг не нужно. А то ты почти в каждом сообщении про приведение типов говоришь, а зачем.
си для меня тяжеловат, я начинал на фортране, потом паскаль потом AutoLisp, там типизация первейшие роли играет... в си конечно все более свободно в плане работы с указателями и памятью...
я все понимаю зачем и чего делается, и для чего можно использовать, но вот конкретика си для меня "не удобна"... например в паскале есть record (с данными переменных типов) а вот как его сделать в си я не понимаю и так далее...
Ну, тяжело - не тяжело, а матчасть учить надо, а то уж больно заумно у Вас всё. Проще будьте.
Есть же микропаскаль от Mikroe или как его там. Или Си не нравится, а для Паскаля нет библиотек? Юзай Паскаль и не парься.
не знаю как в СИ но в паскале было запрещено совместное использование new и malloc, по этому я изначально склонялся к последнему как более универсальному, про освобождение памяти - спасибо (забыл сделать), для меня это не нужно (ибо объект глобальный), но разумеется сделать надо в деструкторе.
-----------------------------------------------------------------------------------
all гуру:
еще вопрос: как лучше решить дилему неопределеного типа у указателя?
собственно чего мне надо в итоге:
есть класс, я в него передаю адрес статической переменной (предположим это глобальная переменная), где-то в классе этот адресс хранится и тут-же хранится тип данных.
далее в объекте есть процедура poll() которая дергается из основного цикла, внутри этой процедуры я получаю по адресу текущее значение и сравниваю его со значением ValueTec который хранится в объекте, если есть разница - вызываю print для LiquidCrystal.
То есть я делаю условно говоря настраиваемый тригер на несколько переменных, собственно проблемма в том, что переменные за которыми мне нужно следить могут иметь различные типы (и следовательно длинну и самое главное знаковость) и следовательно для вызова print мне нужно получить правильно типизированое значение.
я понимаю как можно это сделать наклепав кучу процедур с разным типом параметра (как сделан класс print), но что-то меня это коробит...
может есть где-то готовые реализации подобного или идеи как это реализовать например при ограничении "все переменные только числовые" или "все переменные только целые"?
ps
а вообще сейчас у меня веселый зоопарк из языков, на работе 1с в двух разных версиях, терминал написал и докручиваю на турбодельфи, здесь си, и еще жена с макросами к екселю задолбала :))) давно такого не было, обычно пара и все, а тут 5шт паралельно...
Вас коробит, потому что к паскалю привыкли:) Если не ошибаюсь в дельфях вообще есть функция определяющая тип переменной :)
Только вот я не понял, что вызывает проблему ? Ну есть буфер переменной, ну есть признак типа, известны размеры данных того или иного типа. Делай что хочешь :) Одна беда, в си размер типа byte ясен, а все остальное зависит от разрядности системы, это может стать проблеммой.... Может не стоит извращаться ?
мне не нравится то, что признак типа нужно кодировать числом и передавать в процедуру вместе с адресом...
не знаю как в СИ но в паскале было запрещено совместное использование new и malloc, по этому я изначально склонялся к последнему как более универсальному,
Между ними есть серьёзная разница. Для классов (а структура - это класс) лучше привыкать использовать именно new, т.к. malloc только выделяет память, а new ещё и вызывает конструктор. В данной структуре у Вас нет конструктора, но это ничего не значит - просто привыкните использовать new для классов - это должно быть на автомате. Не раз ещё выручит.
Если полуграмотные прогеры начнут орать, что malloc эффективнее new, посылайте их .... изучать файл <корень IDE>\hardware\arduino\avr\cores\arduino\new.cpp в котором эти самые new определены вот так:
По другим вопросам сейчас посмотрю, могу ли чем-нибудь помочь.
vde69, старался, но не понял задачи. Давайте я буду объяснять, а Вы поправляйте,
У Вас в классе есть, скажем, три переменные разных типов, скажем, int, float и char *. Все они отображатся на экране. Каждая в своём месте. Теперь Вы хотите иметь одну функцию, которая будет принимать новое значение, каким-то образом понимать к какой именно переменной оно относится, сравнивать с текущим значением и. если не равно, то отображать новое значение на экране и, наконец, запоминать новое значение, как текущее.
Вот то, что набрано наклонным шрифтом мне непонятно.
А вообще, эту задачу я бы делал без функции, я бы определил оператор присваивания для своих переменных. Тогда в программе я бы просто присваивал им значения и не парился бы, а сам оператор присваивания делал бы всё, что нужно, включая и отображение нового значения на дисплей.
Ладно, мне надо исчезать. Напишите, что думаете, завтра продолжим.
еще вопрос: как лучше решить дилему неопределеного типа у указателя?
Никак. Тип указателя определен только на этапе компиляции. На этапе исполнения это просто адрес, по крайней мере для простых типов, и без всяких извращений типа смартпоинтеров и т.д.
я понимаю как можно это сделать наклепав кучу процедур с разным типом параметра (как сделан класс print), но что-то меня это коробит...
Вас коробит, потому что к паскалю привыкли:) Если не ошибаюсь в дельфях вообще есть функция определяющая тип переменной :)
Чето такого не припомню. Для обектов можна реализовать если передается экземпляр, а параметром функции описан его предок, внутри которого есть свойство определяющее тип потомка. Тогда по этому свойству и разделяют в функции кто из потомков пришел. В общем, в С++ тоже можно, и активно юзается.
Только вот я не понял, что вызывает проблему ? Ну есть буфер переменной, ну есть признак типа, известны размеры данных того или иного типа. Делай что хочешь :) Одна беда, в си размер типа byte ясен, а все остальное зависит от разрядности системы, это может стать проблеммой.... Может не стоит извращаться ?
я покажу, все предельно локанично:
DT, ds18b20 - это классы которые получают и хранят данные внутри себя
LCD_4X20_I2C - это класс который должен выводить эти данные на экран, для этого в процедуре инициализации я в класс должен передать адреса этих переменных, и я их сохраняю в массиве (массив из структур с адресом, типом, форматом и т.д.), далее по вызову poll я прохожусь по массиву получаю данные и в случае необходимости вывожу на экран только те значения которые поменялись.
давайте весь код.
Влом тип передават - вот и проблема )) А по размеру данных низя вобще никак!!!
целое в 2 байта может быть и int и безнаковое int, по этому одной длинны мало.
делать в классе кейс с 10 вариантами, и описание к классу - класс будет очень некрасивым и недружественным... что приемлемо в программе не приемлемо в классе...
давайте весь код.
так нету всего кода еще,
Блин написал много и глюконуло. Теперь коротко.
Варианты:
1. Сделать общего предка для DT, ds18b20 с нужным полем данных и работать через него.
2. Инитить DT, ds18b20 указателями на элементы массива куда сохранять данные.
3. Передавать в DT, ds18b20 калбеки (или обекты) которые приймут данные и положат и сделают что надо.
4. Перестать мудрить и после получения данных вызыват соответствующий метод LCD_4X20_I2C а уж он разберется, сохранять или выводить.
я так понимаю, хотите SetParam вызвать раз и передать указатель на данные из DT, ds18b20 ?
Если я правильго понял, то получим следующее.
Данные хранятся в DT, ds18b20 в представлении специфичном для каждого класса, и таких классов в принципе может быть много. И у каждого свой тип данных. И что, под каждый из них делать преобразования в Lcd_I2C_4x20 учитывая что он эти типы вообще не знанет? Нелогично. Логичней в DT, ds18b20 и подобных приводить данные к одному типу, например строке, учитывая что будет вывод на экран. И соответственно проблема снимется т.к. в SetParam будут передаватся указатели на эту строку. Можна два представления - строку и число, если надо математику делать в Lcd_I2C_4x20. В таком случае уместны те варианты что я выше писал. Типа родитель для DT, ds18b20 у которого есть GetInt и GetStr..
Но вобще если есть разные форматы одних данных у разных классов, то конвертировать эти фориаты надо внутри этих классов. Инкапсуляция специфического типа данных шоб была.
Кстати, а как в такой архитектуре будете решать проблему ошибки получения данных, например ds18b20 отключили или ошибка crc?
я так понимаю, хотите SetParam вызвать раз и передать указатель на данные из DT, ds18b20 ?
Если я правильго понял, то получим следующее.
Данные хранятся в DT, ds18b20 в представлении специфичном для каждого класса, и таких классов в принципе может быть много. И у каждого свой тип данных. И что, под каждый из них делать преобразования в Lcd_I2C_4x20 учитывая что он эти типы вообще не знанет? Нелогично. Логичней в DT, ds18b20 и подобных приводить данные к одному типу, например строке, учитывая что будет вывод на экран. И соответственно проблема снимется т.к. в SetParam будут передаватся указатели на эту строку. Можна два представления - строку и число, если надо математику делать в Lcd_I2C_4x20. В таком случае уместны те варианты что я выше писал. Типа родитель для DT, ds18b20 у которого есть GetInt и GetStr..
Но вобще если есть разные форматы одних данных у разных классов, то конвертировать эти фориаты надо внутри этих классов. Инкапсуляция специфического типа данных шоб была.
Кстати, а как в такой архитектуре будете решать проблему ошибки получения данных, например ds18b20 отключили или ошибка crc?
Да Вы правильно поняли то что я хочу сделать,
про преобразование данных в родительских класса - не пойдет, ибо они использоваться будут во первых не только для экрана, а во вторых форматов вывода (экранов) будет несколько и разных (и по размерам и по моделям), по этому преобразование к формату вывода логичнее делать в классе который отвечает за вывод...
инкаспуляцию между классов разных предков мне совсем не хочется делать по причинам отрытости и многовариантности сборок (директив много, и поставщиков данных будет много, планирую около 50)
по поводу ошибок - каждый модуль затачиваю под автономность, вот пример, тут реализован авотномный режим
vde69, почитайте вкратце про архитектуру MVC - оно тут немного к месту будет, поясню почему: по сути ваш экран - это и есть View, в которое надо выводить разнородные данные. Модель этих данных может быть различная, контроллер данных - с натяжкой примем, что это ваши классы для получения информации с датчиков.
Так вот, задача отображения данных - это именно задача View. И логичнее некуда делать так, чтобы вся логика работы с выводом переменных разнородного типа во View была выделена отдельно, пусть это будет класс. А вот в этом классе уже для каждого типа данных пишете или операторы, или перегруженные функции. Это нормальный подход, и пусть вас не пугает, что класс будет выглядеть "недружественным" и т.п. Вот кусок кода из моего проекта, например (несущественное вырезал):
Вот примерно ваша задача - хранить разнородные типы данных с датчиков. И ничего страшного в классе нет - так, мутота одна. Зато работает как надо. И если надо ввести новый тип данных - это тоже несложно. Всеми преобразованиями данных класс занимается внутри себя.
про преобразование данных в родительских класса - не пойдет, ибо они использоваться будут во первых не только для экрана, а во вторых форматов вывода (экранов) будет несколько и разных (и по размерам и по моделям), по этому преобразование к формату вывода логичнее делать в классе который отвечает за вывод...
Ниразу не верно! Смотрите, допустим есть 5 классов ввода данных (подобных DT, ds18b20) и 5 классов "экранов". У каждого абсолютно свой формат. Вам прийдется озаботится преобразованиями формата 5*5=25 вариантов. А через единый промежуточный формат (возможно родной для одного или нескольких) 5+5=10 вариантов (минус те у кого он родной).
Мало того наличие 5 сущностей решающих сходную задачу, это четкое показание к ООП (что вобщем уже у вас есть) и выстраиванию их в иерархию классов, тут даже думать не о чем. И 5 экранов и 5 классов ввода данных - все сразу в иерархию с родителями и т.д. Соответственно у родителеу геттер обявлен, у потомков переопределен под конкретную реализацию. Аналогично у экранов. Абсолютно классический случай.
Вот примерно ваша задача - хранить разнородные типы данных с датчиков. И ничего страшного в классе нет - так, мутота одна. Зато работает как надо. И если надо ввести новый тип данных - это тоже несложно. Всеми преобразованиями данных класс занимается внутри себя.
Это мало чего даст. Фактически тот же идентификатор типа(он же Тип указателя, он же признак типа, как в постах выше называли) всеравно надо явно указывать. Просто если ранее данные и методы работы с ними лежали в классе ввода, то теперь они и методы работы с ними в отдельном классе данных, но существует соответствие один класс ввода-один класс данных. Пока это соответствие не нарушается разницы не будет, разве что не желая сводить классы ввода в одну иерархию можна "спрятатся" за этим классом данных.
Это мало чего даст. Фактически тот же идентификатор типа(он же Тип указателя, он же признак типа, как в постах выше называли) всеравно надо явно указывать. Просто если ранее данные и методы работы с ними лежали в классе ввода, то теперь они и методы работы с ними в отдельном классе данных, но существует соответствие один класс ввода-один класс данных. Пока это соответствие не нарушается разницы не будет, разве что не желая сводить классы ввода в одну иерархию можна "спрятатся" за этим классом данных.
Ага, согласен. Именно по этой причине и упомянул про MVC - строго говоря, лобастые дядьки не зря думали в своё время - мухи отдельно, котлеты отдельно. Но всё равно на каком-то этапе мы спускаемся к модели данных (считаю её главной составляющей MVC, к слову). А вот как быть с этой моделью - вопрос серьёзный и нужный. В языках со строгой типизацией всё равно никак не обойтись без идентификатора типа (C# тут, пожалуй, исключение - хоть и строго типизирован, но там есть Reflection, по сути, тот же RTTI, что в Дельфях и C++ деБилдере - через него можно хоть чёрта лысого на свет вытащить) - иначе как понять, с какими данными мы работаем в текущий момент и каким образом интерпретировать содержимое ячеек памяти, в которых содержатся переданные сырые данные?
В общем, это всё теория, лично я предпочитаю особо не париться в таких вот не сильно сложных реализациях - всё равно набор типов данных достаточно ограничен, чтобы забивать себе голову на тему "класс будет некрасиво выглядеть и вообще - недружественно" :) Задача - ехать, а не шашечки.
Хотя да, признаюсь - самого коробит вынужденная избыточность кода, когда речь заходит о подобных "универсальных" вещах.
разве что не желая сводить классы ввода в одну иерархию можна "спрятатся" за этим классом данных.
И да, тут ещё какой ключевой момент серьёзный в сложной иерархии классов, применительно к МК: гадские vtable, которые отжирают оперативу (которой и так не густо). Именно поэтому я все преобразования делаю в одном классе. Два байта тут, байт там - насобирали лукошко :)
Уга. Какраз хотел добавить в свой пост, что все дико громоздко, учитывая что там пару байт данных температуры ))
Вот и я считаю, что для микроконтроллеров универсальность - дело десятое, "ехать важней". Но тут такие хотелки... 50 разных классов... ну фиг его знает...
почитав и поразмыслил я решил
1. оставить хранение данных в классах где они получаются, эти классы вообще не менять... (обзовем эти классы "source")
2. классы которые используют эти данные (обзовем их "use") не должны зависить от реализаций классов "source"
3. между этих классов должны стоять функции обеспечивающие конвертацию типов данных, эти функции будут лежать в отдельном внеклассовом модуле и в них будет куча директив условной компиляции (для вырезания только необходимого кода)
4. настройка класса "use" будет сводится к записи адреса функции конвертера и требуемого формата вывода
недостатком будет - большое количество функций "конвертеров", и необходимость их добавлять при расширении системы
А зачем вам эта условная компиляция в диких кол-вах? Компилятор отлично оптимизирует и вырезает неиспользуемый код.
ну во первых я ретроград, и не сильно доверяю компилятору, а учитывая использования адресов процедур я не сильно уверен в автоматическом результате.
во вторых в этих функциях будут использоватся имена классов и объектов которые могут и не присутствовать, и что-бы банально компилятор не ругался нужно вырезать лишнее
в третьих с вырезками компиляция работает быстрее (по тому как оптимизация происходит в самом конце, а вырезка в самом начале), на больших проектах это ощутимо заметно
Мне не нравится Ваш п №3.
Парни Вам здесь правильно написали, этим должен заниматься специальный класс (интерфеймс) от которого наследуются все классы source.
Это примерно так, как сделан интерфейм Printable в IDE. Все, кто хочет уметь печататься нследуются от Printable, и класс Print их всех отлично понимает.
про создании базового класса:
такой класс будет узконаправленным на конкретику проекта и тогда все его потомки то-же будут узконаправлеными, кроме того метод "правка под проект базового типа" куда хуже чем изменение одного конкретного модуля, хотя-бы по тому, что может повлиять на потомков совершенно не очевидным образом...
мой подход такой основан на создании одного или нескольких стандартизированых интерфейсов между объектами источниками данных и получателями (и без разницы это дисплей или запись в файл или входящие данные для расчетов).
подобный подход широко распространен, например ADO работает по такому прицепу... да и вообще все провайдеры субд... А по сколько я с СУБД в хороших отношениях, то и выбрал этот подход а не инкаспуляцию (которая то-же имеет право на жизнь для решения моей задачи)
Дело Ваше, но это не базовый класс. а интерфейс. Который ничего не делает (как тот же Printable). В нём вообще нет никакиз реальных методов - это интерфейс с единственным абстрактным методом. Вот, смотрите (из текстов IDE):
И всё, там больше нихрена нет. И от него наследуются все, кто хочет печататься. Те, кто от него наследуются обязыаны сами у себя реализовать метод printTo. А функции печати принимают указатель на Printable и тем самым любой класс, унаследованный от этого интерфейса умеет печататься единообразно.
Т.е. Вы вправе делать как хотите, но профессионально это делается вот так.
Блин, ну задача же из ООП в чистом виде. Евгений вам всё правильно написал, куда вас понесло? Стандартизированный подход при работе с разнородными данными - прекрасно реализуется при помощи ООП. На примере класса вывода на дисплей: он наследуется от Print, переопределяет метод print, и внутри этого метода посылает команды по шине для записи на дисплей). Класс HardwareSerial - посылает данные по USART, а не на дисплей - и всё при помощи одного переопределённого метода.
То есть вот вам и интерфейс между входящими данными и их выводом куда-либо: базовый класс с набором абстрактных методов, всё.
ADO, которое вы упомянули, кстати, построена как раз по такому принципу - иерархия наследования. Если вы посмотрите набор классов, то докопаетесь до интерфейса, который должны реализовывать все провайдеры ;)
Вообще не понимаю - в чём у вас проблема? В некрасивости кода? В избыточности кода? В непонимании, как сделать иерархию классов? В основах ООП?
ADO работает между СУБД и клиентом. Разработчики АДО не имеют возможности доработать ни клиента ни СУБД. Если бы могли - ADO не было бы, его интерфейсы реализовывались бы с СУБД :) В МК вы пишете все и нет нужды копировать архитектуру ПО ПК. Это даже вредно, монструозно выходит.
блин, да не понимаете Вы меня...
да я согласен, что принт должен наследоватся (и это у меня именно так и есть), но я разделяю весь процесс на
1. получение и хранение данных
2. вывод данных (тут действительно наследники принта)
3. использование данных не для вывода (а например для расчетов, или для непосредственного управления)
по этому у меня есть 2 разных задачи вывод и расчет, но эти две разные задачи будут опиратся на общий интерфейс работы с данными
конечно можно делать включение света через потомок принта, и даже расчет погоды по туче параметров то-же можно сделать через потомок принта, но я считаю, что везде нужно знать меру, это как нормализация и денормализация в субд, есть правила говорящие что нужно соблюдать основные формы нормализации, но в жизне это не так, есть куча методичек которые говорят о необходимости денормализации таблиц субд, как всегда истина где-то посередине...
мой подход гарантирует понятность промежуточного интерфейса (на выходе функций конверторов) улучшение читабельности, отладки и уменьшение ошибок, конечно то-же самое можно сделать на классе и будет то-же самое, но я буду делать на функциях исходя из необходимости использовать директивы компилятора, которые на классе то-же будут работать, но учитывая, что компилятор кеширует результаты работы можно нарватся на протухший кеш объекта который компилятор схавает, а протухший кеш модуля совсем мало вероятен...
По вашим пунктам 1,2,3 - MVC в чистом виде. Его и реализуйте, дальше обсуждать - ни о чём, всё уже придумано за нас.
Вот, в общем: https://ru.wikipedia.org/wiki/Model-View-Controller
Ну, удачи Вам!
собственно отчитываюсь - все заработало, единственное - были заморочки с видимостью, но все решилось нормально...
конвертер:
инициализация экрана:
сам класс
описание структур
del