Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Объектно-ориентированное программирование на C  (Прочитано 17239 раз)
0 Пользователей и 1 Гость смотрят эту тему.
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« : 28-01-2011 15:46 » 

Цитата: Dale
Без всякого преувеличения книгу можно назвать уникальной. Шутка ли, автор учит объектному подходу к проектированию программ, которые впоследствии реализуются на языке C! В это невозможно поверить, пока сам не проследишь за каждым шагом фокуса и не убедишься, что все без обмана.
Я учу Улыбаюсь У меня и вопрос на экзамене есть соответствующий. Но книжки не публиковал - это да, так что, наверно, уникальная.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dale
Блюзмен
Модератор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #1 : 29-01-2011 02:41 » 

Так в том и штука: в неопубликованных книгах есть абсолютно все, только вот как их читать и набираться ума-разума? Улыбаюсь А опубликованных до обидного мало...

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

Цитата: Dale
Без всякого преувеличения книгу можно назвать уникальной. Шутка ли, автор учит объектному подходу к проектированию программ, которые впоследствии реализуются на языке C! В это невозможно поверить, пока сам не проследишь за каждым шагом фокуса и не убедишься, что все без обмана.
Я учу Улыбаюсь

А методички для студентов есть? Интересно сравнить ваши рекомендации по созданию открытых/закрытых членов класса/экземпляра, виртуализации/наследованию, рефакторингу и тестированию, TDD и пр. на С.
« Последнее редактирование: 29-01-2011 04:28 от Dale » Записан

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

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #2 : 29-01-2011 13:56 » 

Цитата: Dale
Интересно сравнить ваши рекомендации по созданию открытых/закрытых членов класса/экземпляра, виртуализации/наследованию, рефакторингу и тестированию, TDD и пр. на С.
Столь детально разбирать вопрос, чтобы была методичка, у меня времени не хватает - всё же основная тема ООП, а не то, как в рамках ООП использовать C. Т.е. я даю этот вопрос для общего развития студентов и углубления понимания ими того, как работают объектные программы - это средство, а не самостоятельная цель в курсе ООП.

По этой же причине рефакторинг и тестирование идёт мимо.

Инкапсуляция. Я советую каждый класс оформлять отдельной парой h- и c-файлов с соответствующими названиями (в духе Java/C#). В h-файл выносить открытое, в c-файл - закрытое. Есть некоторые сложности со структурами. Чтобы не показывать их содержимое, нужно использовать только указатели на них - не всегда это удобно.

Атрибуты и методы класса задаются как глобальные переменные и обычные функции.

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

Наследование. Наследование данных - через агрегацию структур. Наследование функций - через делегирование вызовов.
Код: (C)
/*
class A
{
private:
  int a;
public:
  A();
  void f();
};

class B: public A
{
private:
  int b;
public:
  B();
};
*/


struct A
{
  int a;
};

struct A *A_constructor()
{
  struct A *instance = (struct A)malloc(sizeof(struct A));
  instance->a = 0;
  return instance;
}

void A_destructor(struct A *this)
{
  free(this);
}

void A_f(struct A *this)
{
  printf("%i", this->a);
}

struct B
{
  struct A *baseA;
  int b;
}

struct B *B_constructor()
{
  struct B *instance = (struct B)malloc(sizeof(struct B));
  instance->baseA = A_constructor();
  instance->b = 0;
  return instance;
}

void B_destructor(struct B *this)
{
  A_destructor(this->baseA);
  free(this);
}

void B_f(struct B *this)
{
  A_f(this->baseA);
}
Поскольку конструкции здесь во многом "регулярные", при желании их можно оформить макросами. А договорившись об отказе от множественного наследования, и ссылку на предка можно сделать унифицированной.

Виртуальные методы - вручную реализовать таблицы виртуальных функций.

Вышеприведённый пример с контролем типов годится лишь для таких потомков, которые не имеют своих данных и поэтому не используют агрегацию структуры предка в структуре потомка.
Код: (C)
struct A;
typedef void (*A_F)(struct A *this);
struct A
{
  int a;
  A_F f;
};

void A_f(struct A *this);
struct A *A_constructor()
{
  struct A *instance = (struct A)malloc(sizeof(struct A));
  instance->a = 0;
  instance->f = A_f;
  return instance;
}

void A_destructor(struct A *this)
{
  free(this);
}

void A_f(struct A *this)
{
  printf("%i", this->a);
}

typedef struct B struct A;

void B_f(struct B *this);
struct B *B_constructor()
{
  struct A *instance = A_constructor();
  instance->f = B_f;
  return instance;
}

void B_destructor(struct B *this)
{
  A_destructor(this);
}

void B_f(struct B *this)
{
  printf("Hello world");
}

int main()
{
  struct B *b = B_constructor();
  b->f(b);
  B_destructor(b);
  return 0;
}

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

Но вообще-то комбинирование наследования данных и виртуальных функций не особенно часто нужно. В C++ и других языках, где синтаксически записать наследование очень просто, злоупотребляют этим средством. Классы в иерархии логически должны относиться к одной сущности как общее и частное. В большинстве случаев: либо имеется ассоциативная связь двух сущностей - тогда правильнее использовать делегирование, а не виртуальные функции; либо требуется обеспечить динамический полиморфизм необобщающихся по реализации методов - тогда нужны абстрактные методы или в общем случае интерфейсы. А интерфейс есть ни что иное, как выделенная в отдельную структуру таблица виртуальных функций, которой придали самостоятельный смысл - она описывает отдельный протокол взаимодействия с объектом в некоторой ассоциации, в которой этот объект играет определённую роль.
« Последнее редактирование: 29-01-2011 14:00 от Dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dale
Блюзмен
Модератор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #3 : 29-01-2011 14:41 » 

Понятно, спасибо.

Как я и ожидал, разница лишь во второстепенных деталях - у Греннинга "конструктор" не выделяет память экземпляру из кучи, поскольку возможно (и даже более предпочтительно для МК, для которых лучше не затевать игры с динамическим выделением памяти в условиях ее острого дефицита) расположение экземпляра в стеке или области статических переменных. В целом довольно точное совпадение.
« Последнее редактирование: 31-01-2011 06:28 от Dale » Записан

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

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #4 : 31-01-2011 05:03 » 

Люди говорят можно поглядеть на glib в которой вроде как ООП на plain C сделано.
Записан

Странно всё это....
Dale
Блюзмен
Модератор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #5 : 31-01-2011 06:25 » 

В принципе даже нет необходимости постигать эту тему через реверс-инжиниринг. Есть книга, которую автор выложил в Сеть бесплатно: Object oriented programming with ANSI-C ( https://ritdml.rit.edu/handle/1850/8544 ).

Только само по себе ООП на С мало что дает. Книга Греннинга тем и хороша, что в ней сводятся воедино ООП, модульное тестирование и TDD, а также ряд паттернов тестирования, и все это для встроенных систем.
Записан

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

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #6 : 31-01-2011 06:50 » 

1993 год. Информация не стареет Улыбаюсь
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Dale
Блюзмен
Модератор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #7 : 31-01-2011 07:13 » new

В информатике это обычное явление Улыбаюсь

Новаторы что-то придумывают, а инертная масса дозревает еще 10, а то и 20 лет. Вспомните хотя бы недавнюю дискуссию, где один товарищ с жаром доказывал, что еще в середине 90-х вовсю использовали GOTO, и это нормально. Сам видел, как в 80-х вовсю писали программы на FORTRAN-IV, хотя были доступны Pascal, Modula-2 и C. А сколько у нас на форуме вопросов по Borland C 3.1 И TurboPascal?

А сейчас массы начинают открывать для себя ООП, которому уже лет этак 30. Не успевают умы за прогрессом железа...
Записан

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

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines