реклама на сайте
подробности

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
> инкапсуляция в С, как?
Dubov
сообщение Apr 18 2014, 12:01
Сообщение #1


Местный
***

Группа: Участник
Сообщений: 408
Регистрация: 28-05-12
Пользователь №: 72 052



Хочется писать красивый и правильный код.

Сейчас активно использую глобальные переменные и типа, если надо чтобы один модуль записывал данные, а другой их читал, то объявляю буфер как
extern buf[256];

Вобщем понимаю что не комильфо всё это. Но иные варианты сами в голову не приходят, прошу помочь.
Go to the top of the page
 
+Quote Post
aaarrr
сообщение Apr 18 2014, 12:06
Сообщение #2


Гуру
******

Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448



Цитата(Dubov @ Apr 18 2014, 16:01) *
extern buf[256];

Сначала надо определиться, что именно здесь некрасиво и неправильно. Я, например, не вижу проблем.
Go to the top of the page
 
+Quote Post
Dubov
сообщение Apr 18 2014, 12:17
Сообщение #3


Местный
***

Группа: Участник
Сообщений: 408
Регистрация: 28-05-12
Пользователь №: 72 052



Цитата(aaarrr @ Apr 18 2014, 16:06) *
Сначала надо определиться, что именно здесь некрасиво и неправильно. Я, например, не вижу проблем.

проблема в том, что буфер объявлен как глобальный в одном модуле proc.c, а в другом модуле main.c происходит его чтение, и там он объявлен как extern buf

ну и собственно глобальные переменные - это не правильно.

Разве нет? rolleyes.gif
Go to the top of the page
 
+Quote Post
aaarrr
сообщение Apr 18 2014, 12:21
Сообщение #4


Гуру
******

Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448



Цитата(Dubov @ Apr 18 2014, 16:17) *
ну и собственно глобальные переменные - это не правильно.

Разве нет? rolleyes.gif

Нет.
Go to the top of the page
 
+Quote Post
Swup
сообщение Apr 18 2014, 12:31
Сообщение #5


Частый гость
**

Группа: Свой
Сообщений: 127
Регистрация: 2-09-11
Из: Москва
Пользователь №: 66 970



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

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

Сообщение отредактировал Swup - Apr 18 2014, 12:32
Go to the top of the page
 
+Quote Post
insane_person
сообщение Apr 18 2014, 15:40
Сообщение #6





Группа: Участник
Сообщений: 6
Регистрация: 20-04-13
Пользователь №: 76 551



Это увеличивает связность кода и соответственно усложняет его понимание.
Как по мне так лучше в одном файле обьявить static array.
И написать функции работы с ним (чтение и запись), которые будут видны в других файлах.
Так гораздо удобнее отлавливать баги связанные с массивом, и проще понять где было произведено чтение или модификация массива.
Go to the top of the page
 
+Quote Post
Xenia
сообщение Apr 18 2014, 16:33
Сообщение #7


Гуру
******

Группа: Модератор FTP
Сообщений: 4 479
Регистрация: 20-02-08
Из: Москва
Пользователь №: 35 237



Цитата(Dubov @ Apr 18 2014, 16:01) *
Хочется писать красивый и правильный код.
Сейчас активно использую глобальные переменные и типа, если надо чтобы один модуль записывал данные, а другой их читал, то объявляю буфер как
extern buf[256];


Если надо красиво и правильно, но все объявления extern-объектов следует делать в отдельном хидере.
Например, в
global.h
куда вписать все extern-определения.

А чтобы и сами глобальные объекты не расползлись по разным модулям, то их желательно тоже поместить в отдельную корзину.
Например, собрать их всех в модуле
global.c
или завести себе правило объявлять глобальные переменные только в модуле main.c
(тогда и global.h можно обозвать main.h).

А в самих сишных модулях слово extern никогда (!) не должно встречаться, а допустимо лишь включать
#include "global.h"
или
#include "main.h"

Тогда уж вы точно избежите любых ошибок, связанных с недоопределением глобальных переменных.
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Apr 18 2014, 18:54
Сообщение #8


Гуру
******

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



Цитата(Xenia @ Apr 18 2014, 19:33) *
Если надо красиво и правильно, но все объявления extern-объектов следует делать в отдельном хидере.
Например, в
global.h
куда вписать все extern-определения.
Ага. Свалим все в одну кучу, сделаем одну большую тарелку с макаронами, в которой станет почти невозможно отделить мух от котлет. Афигенно красиво. И настолько же правильно. cranky.gif


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
Xenia
сообщение Apr 18 2014, 22:13
Сообщение #9


Гуру
******

Группа: Модератор FTP
Сообщений: 4 479
Регистрация: 20-02-08
Из: Москва
Пользователь №: 35 237



Цитата(Сергей Борщ @ Apr 18 2014, 22:54) *
Ага. Свалим все в одну кучу, сделаем одну большую тарелку с макаронами, в которой станет почти невозможно отделить мух от котлет. Афигенно красиво. И настолько же правильно. cranky.gif


Именно так. Определения, которые используются более чем одним модулем, должны не повторяться в каждом модуле, а быть однократно определены в хидере. Именно для этой цели хидеры изобрели - чтобы обеспечивать единообразие определений во всех модулях программы.
Go to the top of the page
 
+Quote Post
Tiro
сообщение Apr 18 2014, 22:25
Сообщение #10


Знающий
****

Группа: Свой
Сообщений: 781
Регистрация: 3-10-04
Из: Санкт-Петербург
Пользователь №: 768



Цитата(Xenia @ Apr 18 2014, 19:33) *
Если надо красиво и правильно, но все объявления extern-объектов следует делать в отдельном хидере.
А чтобы и сами глобальные объекты не расползлись по разным модулям, то их желательно тоже поместить в отдельную корзину.
Например, собрать их всех в модуле
global.c

Упаси Боже так делать sm.gif)
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Apr 18 2014, 23:36
Сообщение #11


Гуру
******

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



Цитата(Xenia @ Apr 19 2014, 01:13) *
Именно так. Определения, которые используются более чем одним модулем, должны не повторяться в каждом модуле, а быть однократно определены в хидере.
А вот и нет. Во-первых не определения, а объявления. А во-вторых переменные модуля А и его функции, которые используются другими модулями, должны быть объявлены в его заголовочном файле A.h и именно этот заголовочный файл должен включаться в тот и только в тот исходник/заголовочный файл, где эти объявления используются. А валить все в одну кучу, в один глобальный файл - это кошмар для сопровождения проекта.


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
Xenia
сообщение Apr 19 2014, 03:39
Сообщение #12


Гуру
******

Группа: Модератор FTP
Сообщений: 4 479
Регистрация: 20-02-08
Из: Москва
Пользователь №: 35 237



Цитата(Сергей Борщ @ Apr 19 2014, 03:36) *
А вот и нет. Во-первых не определения, а объявления.

Ну, путь объявления. sm.gif

Цитата(Сергей Борщ @ Apr 19 2014, 03:36) *
А во-вторых переменные модуля А и его функции, которые используются другими модулями, должны быть объявлены в его заголовочном файле A.h и именно этот заголовочный файл должен включаться в тот и только в тот исходник/заголовочный файл, где эти объявления используются. А валить все в одну кучу, в один глобальный файл - это кошмар для сопровождения проекта.

A.h это само собой. Ничто не мешает каждому модулю иметь свой хидер, в котором объявлять те функции, которые в нем определяются. А хидер global.h подключается дополнительно. Ведь и без того одним хидер-файлом не обойдешься.
Go to the top of the page
 
+Quote Post
=AK=
сообщение Apr 19 2014, 04:01
Сообщение #13


pontificator
******

Группа: Свой
Сообщений: 3 055
Регистрация: 8-02-05
Из: страны Оз
Пользователь №: 2 483



Цитата(Dubov @ Apr 18 2014, 21:31) *
Хочется писать красивый и правильный код.

Используйте структуры. Возьмите себе за правило, что любая глобальная переменная должна быть частью какой-то структуры, а у структур должны быть короткие и осмысленные имена, чтобы по имени легко можно было понять, в каком модуле эта структура определена и в основном используется. Не используйте глобальные переменные, не входящие в структуры, используйте такие переменные только локально.
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Apr 19 2014, 04:05
Сообщение #14


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



Я тоже без особой нужды ничего не открываю. Доступ извне к массиву, объявленному в модуле - через функцию. Это не засоряет пространство имен внутри модуля.
Хоть 100500 одинаково названных queue, но определенных в разных модулях.
Go to the top of the page
 
+Quote Post
=AK=
сообщение Apr 19 2014, 04:34
Сообщение #15


pontificator
******

Группа: Свой
Сообщений: 3 055
Регистрация: 8-02-05
Из: страны Оз
Пользователь №: 2 483



Цитата(_Pasha @ Apr 19 2014, 13:35) *
Доступ извне к массиву, объявленному в модуле - через функцию. Это не засоряет пространство имен внутри модуля. Хоть 100500 одинаково названных queue, но определенных в разных модулях.

Такой подход имеет право на существование, однако у него все же есть два недостатка:
- Пространство имен функций все-таки засоряется. Не получится иметь 100500 функций init();
- На вызов функции и возврат из нее тратится время и память кода. Напрямую с переменной работать эффективнее.
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Apr 19 2014, 07:31
Сообщение #16


Гуру
******

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



Цитата(Xenia @ Apr 19 2014, 06:39) *
А хидер global.h подключается дополнительно. Ведь и без того одним хидер-файлом не обойдешься.
Зачем, ну объясните мне, зачем делать эту общую свалку global.h? Только ради того, чтобы в каждом исходнике писать одну строчку #include "global.h" вместо нескольких, по которым будет совершенно однозначно видно, какие именно другие модули использует этот? И какие файлы надо захватить вместе с этим при копировании этого модуля в другой проект?

И что-то я не понял насчет "подключается дополнительно". Если уже подключен заголовочный файл модуля А.h, то включены все необходимые объявления модуля A. Что тогда будет в global.h?


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Apr 19 2014, 09:32
Сообщение #17


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



Цитата(=AK= @ Apr 19 2014, 07:34) *
Такой подход имеет право на существование, однако у него все же есть два недостатка:
- Пространство имен функций все-таки засоряется. Не получится иметь 100500 функций init();
- На вызов функции и возврат из нее тратится время и память кода. Напрямую с переменной работать эффективнее.

Со временем устаканилось две вещи (как ответ на "два недостатка"):
1. Там, где надо 100500 ИНИТов делать - это решается через хэндлы. Как говорят военные, безобразно, зато единообразно.
Код
struct _handle
{
  void (*init)(void);
  void (*done)(void);
};

2. Там, где требуется оченно зашкаливающая эффективность, - там нет таких объемов данных и все сосредоточено в пределах одного модуля.
Это все фигня, потому что С++ все-таки дает неквадратные колеса. Если человеку аж чешется ООП, то для чего этим страдать на Си? На Си даже try - catch можно делать, ну и?
На GCC можно даже частично RTTI на макросах делать. Но к чему?


Сообщение отредактировал _Pasha - Apr 19 2014, 09:38
Go to the top of the page
 
+Quote Post
andrewlekar
сообщение Apr 20 2014, 15:50
Сообщение #18


Знающий
****

Группа: Участник
Сообщений: 837
Регистрация: 8-02-07
Пользователь №: 25 163



Цитата
Зачем, ну объясните мне, зачем делать эту общую свалку global.h?

Чтобы пихать туда вещи, которые не принадлежат ни к одному модулю. Глобальные переменные например.
В глобально доступных переменных нет сильно большого криминала до тех пор, пока к ним доступ осуществляется атомарно в многопоточных программах. И пока не страдает логика приложения, например всё приложение начинает управляться глобальными флагами.
Go to the top of the page
 
+Quote Post
Dubov
сообщение Apr 22 2014, 05:32
Сообщение #19


Местный
***

Группа: Участник
Сообщений: 408
Регистрация: 28-05-12
Пользователь №: 72 052



Цитата(andrewlekar @ Apr 20 2014, 19:50) *
Чтобы пихать туда вещи, которые не принадлежат ни к одному модулю. Глобальные переменные например.
В глобально доступных переменных нет сильно большого криминала до тех пор, пока к ним доступ осуществляется атомарно в многопоточных программах. И пока не страдает логика приложения, например всё приложение начинает управляться глобальными флагами.


Вот и я о том. когда всё приложение сводится к
while(1)
и опросу глобальных флагов
if (x == 1)

то это не хорошо

как избежать?
Go to the top of the page
 
+Quote Post
andrewlekar
сообщение Apr 22 2014, 07:58
Сообщение #20


Знающий
****

Группа: Участник
Сообщений: 837
Регистрация: 8-02-07
Пользователь №: 25 163



Приложение может строиться разными способами. Ваша задача подобрать такой способ, который будет удобен для вас и расширяем для потенциального роста приложения.
1. Либо это суперлуп (главный цикл) с флагами. Для небольших приложений.
2. Либо это взаимодействие конечных автоматов. Для приложений чуть побольше.
3. Традиционный многопоточный подход с обменом сообщениями между потоками. Почти любого масштаба приложений, но требует высокой квалификации программиста.
4. Всякие вариации в сторону декларативного и функционального программирования. Требует поддержки от компилятора.

Это я навскидку составил список. Если упростить, то нужно делать по максимуму модульную программу. Там где это возможно, глобальные флаги заменять на вызов функции соответствующего модуля (например uart_is_data_ready()). Для передачи массива данных делать копию для обработки в подходящем модуле. Граф зависимостей модулей не должен (не желательно) иметь циклы. Если модуль A зависит от модуля Б, а модуль Б от модуля А, то цикл нужно разорвать.
Go to the top of the page
 
+Quote Post
thodnev
сообщение May 8 2014, 16:48
Сообщение #21


Участник
*

Группа: Участник
Сообщений: 44
Регистрация: 20-01-13
Из: Украина, Киев
Пользователь №: 75 259



Инкапсуляция является одним из подходов к ООП, а С как известно к таким языкам не относится.
Тем не менее, пару раз попадался красивый код на чистом С, реализующий инкапсуляцию(поразила красивая работа со структурами).

В Вашем конкретном случае с буффером, можно например передавать в функцию указатель на другую функцию, которая и будет вызываться. А вообще, лучше использовать С++
Go to the top of the page
 
+Quote Post

2 страниц V   1 2 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 29th July 2025 - 00:55
Рейтинг@Mail.ru


Страница сгенерированна за 0.02182 секунд с 7
ELECTRONIX ©2004-2016