|
2 вопроса по Си++, WinAVR |
|
|
|
Aug 18 2008, 13:06
|
Местный
  
Группа: Свой
Сообщений: 466
Регистрация: 21-06-05
Пользователь №: 6 205

|
Вопрос 1: Есть функция getErrorPacket(), она является частью класса Etaz_item, который в конструкторе принимает ссылку на класс типа NetTransferConstrol, который уже в свою очередь содержит функцию get_error_packet(), которая ""генерирует" исходные данные. Упрощенно класс Etaz_item выглядит следующим образом Код class NetTransferConstrol;
class Etaz_item: public TechObj { private: NetTransferConstrol & cBfppClass; public: void func1(); void func2(); uint8_t getErrorPacket() { return cBfppClass.get_error_packet(); }; Etaz_item ( NetTransferConstrol & _cBfppClass ) : cBfppClass ( _cBfppClass ) { //....... }; }; т.е. есть какой-то класс NetTransferConstrol, описание которого находится где-то в другом месте, и сам класс Etaz_item, который принимает ссылку на обьект типа NetTransferConstrol. К классу Etaz_item относится 2 файла : Etaz.cpp, где описан код реализации всех функций класса, и Etaz.h, где находится реализация структуры класса. И вот тут начинаются непонятные вещи, когда я код функции getErrorPacket() помещаю в Etaz.cpp, то проект компилируется, но если я перемещая код функции в класс, как показано в примере, то компилятор начинает ругаться Цитата ./src/Etaz.h:166: error: invalid use of incomplete type 'struct NetTransferConstrol' ./src/Etaz.h:122: error: forward declaration of 'struct NetTransferConstrol' Почему компилятор генерирует ошибку в случае когда код функции описан прямо в классе, но ничего не выдает, когда эта же функция описана во внешнем файле(Etaz.cpp)? Возможно как-то получается ситуация когда класс типа Etaz_item "родился", а класс типа NetTransferConstrol еще нет, но тогда совсем непонятно как оно работает и где оно берет ссылку... И непонятно причем тут " struct NetTransferConstrol", ведь NetTransferConstrol обьявлен как class? Пробовал имитировать подобную ситуацию в MinGW, но там все компилируется без проблем, так что считаю что это не синтаксическая ошибка... Вопрос 2: Как мне убедится что на момент создания класса Etaz_item, обьект типа NetTransferConstrol уже существует, и ссылка будет правильной? Всем заранее спасибо за помощь!!! З.Ы. Компилятор WinAVR 20080610 З.Ы.Ы. Обьекты создаются статически во время начальной инициализации
|
|
|
|
|
Aug 19 2008, 04:32
|

Группа: Новичок
Сообщений: 13
Регистрация: 11-08-05
Пользователь №: 7 546

|
Я бы так писал: Код #include <NetTransferConstrol>
class Etaz_item: public TechObj { private: NetTransferConstrol * cBfppClass; public: void func1(); void func2(); uint8_t getErrorPacket() { return cBfppClass->get_error_packet(); }; Etaz_item ( NetTransferConstrol & _cBfppClass ) : cBfppClass ( &_cBfppClass ) { //....... }; };
|
|
|
|
|
Aug 20 2008, 05:54
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(kurtis @ Aug 18 2008, 16:06)  Вопрос 1: Почему компилятор генерирует ошибку в случае когда код функции описан прямо в классе, но ничего не выдает, когда эта же функция описана во внешнем файле(Etaz.cpp)? видимо потому, что на момент, когда компилятор встречает тело функции в файле Etaz.cpp ему уже в каком-то из .h встретилось полное описание NetTransferConstrol. А когда вы переносите тело функции в описание класса - компилятор не встречает полного описания NetTransferConstrol. Цитата(kurtis @ Aug 18 2008, 16:06)  Вопрос 2: Как мне убедится что на момент создания класса Etaz_item, обьект типа NetTransferConstrol уже существует, и ссылка будет правильной? Это головная боль... Железное решение - сделать NetTransferConstrol членом Etaz_item. Или проверять указатель на него. Не могу гарантировать, но кажется встречал у Страуструпа - статические объекты в одной единице компиляции создаются в порядке их объявления. Поищите это в Стандарте. Цитата(Amper25 @ Aug 18 2008, 16:31)  Ох уж эти HighLevel программеры. Ну зачем для MCU, тем более для AVR использовать объектное программирование? Не поверите - чтобы использовать преимущества объектного программирования. Цитата(Amper25 @ Aug 18 2008, 16:31)  хотя я не очень силен в HighLevel программинге. Это действительно сильный аргумент, чтобы не использовать самому.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Aug 20 2008, 07:59
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(Kuzmi4 @ Aug 20 2008, 10:30)  2 Сергей Борщ Не могли бы вы привести тогда пару примеров ? Легко. Вот только некоторые: Инкапсуляция - группирование взаимосвязанных элементов (данные, функции для работы именно с этими данными) в один класс и ограничение доступа к этим данным и функциям извне. Пространства имен - тоже группирование и уменьшение писанины. Наследование - построение иерархии данных. Шаблоны - вместо copy-paste и мелких правок похожих функций пишется шаблон. Переопределение операторов и функций: Код class uart_t public: void dump(uint8_t ); void dump(uint16_t ); void dump(char const *); void dump(my_struct_t *); }; struct my_struct_t { ...... bool operator=(mystruct &); }; вместо void uart_dump_uint8_t(uint8_t); void uart_dump_uint16_t(uint16_t); void uart_dump_string(char const *); void uart_dump_mystruct(my_struct_t *); bool compare_my_struct(my_struct_t *, my_struct_t *); Использование принципа "создание объекта есть инициализация" - когда некое начальное действие (например - запрещение прерываний или формирование заголовка пакета) происходит при создании объекта в его конструкторе и некторое завершающее действие (например - разрешение прерываний или формирование окончания пакета) в его деструкторе. Просто создаете объект и уверены, что после выхода объекта из зоны видимости завершающее действие будет сделано. При обычном подходе вам надо завершающее действие тоже прописать вручную. Это можно забыть сделать, особенно если функция имеет несколько точек выхода, в каждой из которых это действие должно быть выполнено. Все это не приводит к генерации лишнего кода, но дает повышение читаемости исходника и часто уберегает от чисто механических описок (например при тиражировании похожего кода можно забыть внести измение в одну из копий, с шаблоном такое просто невозможно физически). Теперь, возможно, Amper25 приведет описание ужасов объектного подхода, из-за которых "для MCU, тем более для AVR" не нужно его использовать?
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Aug 20 2008, 09:06
|
Местный
  
Группа: Свой
Сообщений: 215
Регистрация: 10-04-07
Пользователь №: 26 929

|
Ну что поаргументирую тут...
"Ужасов" конечно никаких нет. Но в "оправдание" могу привести: 1.Когда пишеш в "процедурном" стиле, всегда знаеш во что это откомпилируется. А выполняя: MY_CLASS *MC_PTR = new(MY_CLASS); ХЗ сколько времени и памяти уйдет на инициализацию, надо читать конструктор.
2.Используя объекты, при создании нового, все внутренние переменные класса помещаются в Heap. А если нам не нужны все они, а мы лиш только хотим выполнить putchar() и закрыть класс?
3.Касательно AVR, то для самых навороченных с 4k и даже аж 8k :)RAM как то вера не позволяет разбазаривать её на право и налево.
4.Для других MCU, и тем более в RTOS конечно объектных подход более приемлим. И не могу не согласится, с тем что код лучше читаем. Тут Сергей Борщ прав.
PS: кстати,
void dump(uint8_t ); void dump(uint16_t ); void dump(char const *); void dump(my_struct_t *);
никто не запрещает сделать C++ -шной перезагрузкой функций, безо всяких объектов.
Да, забыл упомянуть более легкую портабельность кода при использовании объектов.
|
|
|
|
|
Aug 20 2008, 11:00
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Цитата(Amper25 @ Aug 20 2008, 12:06)  1.Когда пишеш в "процедурном" стиле, всегда знаеш во что это откомпилируется. А выполняя: MY_CLASS *MC_PTR = new(MY_CLASS); ХЗ сколько времени и памяти уйдет на инициализацию, надо читать конструктор. Дежавю какое-то :-) Когда лет 12 назад в RU.EMBEDDED нас несколько человек "уговаривали" народ пользоваться С, наиболее часто встречающимся контраргументом было именно это - "когда я пишу на асме, я знаю что будет, а написав строку на си ХЗ сколько времени и памяти..." Цитата(Amper25 @ Aug 20 2008, 12:06)  2.Используя объекты, при создании нового, все внутренние переменные класса помещаются в Heap. А если нам не нужны все они, а мы лиш только хотим выполнить putchar() и закрыть класс? Ну знаете, если на С работу с UART построить таким образом, что на каждый чих будут malloc-ом выделяться байт для временного размещения передаваемого символа и т.п., то тоже никакой памяти не хватит. Как по мне - если в изделии используется UART, то нет никакой необходимости его "открывать-закрывать", если для UART используются буфера FIFO, то и на С они выделяются статически, равно как и переменные, связанные с протоколом, если таковые имеются, а не malloc-аются/free-шаются постоянно. Почему надо С++ ставить в заведомо неравные условия? Создавайте экземпляр класса UART статически и не будет вообще подшиваться код системы new/delete, памяти выделится столько же, сколько при равном функционале на С. Цитата(Amper25 @ Aug 20 2008, 12:06)  3.Касательно AVR, то для самых навороченных с 4k и даже аж 8k :)RAM как то вера не позволяет разбазаривать её на право и налево. Вот то же самое говорилось и в asm vs C, только на уровне "даже для самых навороченных с 256 или даже 512 байтов" :-)
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|