В целом, в Си-шную структуру такие указатели на функции впендюриваются "на раз" и в общем-то необходимость в виртуальных методах отпадает или становится не значительной. А без виртуальности С++, как ЕвгенийП уже замечал ранее - это "Си с классами", которые .. ага, по компактности кода практически равны "С со структурами". :)
Верно излагаеш. Добавлю тока что активная работа с указателями на функции - офигенно мощная штука. Даже самое мощное что есть в чистых сях. Её использовоние покрывает всю виртуальность плюс еще много разного интересного дает. И походу это самое опасное. Неаккуратность валит програму как два пальца. Вот потому вокруг него и накрутили ООП. Чтоб с одной стороны заюзать неопытными пользователями, заменив им понимание работы процессора на догмы ООП, а с другой стороны спрятав указатели в VMT более-менее обезопасить технологию. Ну а для пипла, чтоб прикрыть, что осетра им урезали, служат рассказы про более высокий уровень абстрагирования и строгое разграничение. Что и слышим. Абстрагирование - оно в голове и от неё зависит, а разграничение на модули и либки намного строже получается чем на классы.
Про экономию ресурсов и указатели на функции. Классическая машинка состояния, свитч, многоэтажные кейсы... Загоняем кейсы в функции, а их указатели в массив в PROGMEM и вместо длинючего свитча по некоторому id получаем нечто типа ArrFunc[id](). А если лямбда-функции использовать, то и лишних имен придумывать не прийдется.
А мне вааще Си - нравится с того самого первого моменту, когда произошла эта чудесная встреча .. вплоть до момента, когда пришло осознание что на нем можно ваять (ну почти) всё и практически так как захотел на так любимом мною прежде Ассемблере .. нельзя только одну мелочь: прямая работа с флагом переноса и рядом других флагов. Вот нарисовать на Си арифметику с переносом (скажем трехбайтовых целых) - не получится.
Точно такжеж и тут: посмотрев и освоив С++ в свое время .. внезапно понял, что ничего, акромя "синтаксического сахара" в нем нет. Но, для новичков пугать ООП оказалось очень даже полезно... потом за 17-и лдетним перерывом оно забылось и всплыло совсем не так давно. В смысле понимание, что на Си можно писать практически ВСЁ и как на "жабаскрипт" и как ООП и много лучше. :)
(* ещё бы препроцессор допилить на предмет некоторых упущений, но и без них неплохо *)
По поводу экономии ресурсов: никакой "уровень абстракции вверх" не позволяет этого делать. А вот "внятно" (и быстро) можно писать на чем угодно - дело привычки и культуры писателя. :)
DIYMAN, просвети, реально в С++ можно наследовать класс от структуры?
А они ничем не отличаются ;) Структура - это то же самое, что
class A
{
public:
....
};
В структуре модификатор доступа по умолчанию ко всем членам - public. Но ничего не мешает и там разделить на private, protected и public. Короче, ключевое слово struct в C++ - это класс со всеми открытыми членами по умолчанию. Я просто юзаю их для экономия кол-ва нажатий клавиш :), когда это необходимо.
Кстати, о goto, я тут как-то уже писал о таком замечательном языке, как INTERCAL. В нём нет goto (от слова вообще). Зато там есть противоположный оператор - comefrom (получить управление из) - замечательная вещь!
Наследовать классы (class) от структур (record) в Delphi нельзя, потому что эти типы принципиально разные, не так как в C++.
НО! Немногие знают, что в Delphi есть структуры, практически аналогичные классам C++ (за исключением ряда нюансов). Это объекты (object). Этот тип достался по наследству от Turbo Pascal, и до сих пор поддерживается в современных версиях Delphi. Есть наследование, конструкторы/деструкторы, простые и виртуальные методы. Память для экземпляра может выделяться как статически, так и динамически (классы Delphi - только динамически).
Практическая польза от object в современном коде может быть там, где требуется структурный тип, который можно наложить на любую память, но более развитый, чем record. В частности, в структурах данных при реализации протоколов удобно применять наследование:
type
TFrameHead = object
Sync : byte;
Cmd : byte;
end;
TAnswer = object(TFrameHead)
Res : byte;
Stat : byte;
end;
TFuncRq = object(TFrameHead)
Func : byte;
end;
TDataFuncRq = object(TFuncRq)
Pad : byte;
Len : byte;
Data : record end;
end;
TClassRq = object(TFrameHead)
Pad : byte;
Len : word;
Offs : word;
Code : byte;
end;
TClassAns = object(TAnswer)
Len : byte;
Data : record end;
end;
Отсюда и вопрос: выбор класса, как некоего враппера - это выбор нормальный или есть получше?
Задача у меня, конечно, позаковыристей: есть два драйвера, немного отличаются методами. Надо было сделать так, чтобы из основного кода дергать всё однотипно и держать некоторое кол-во метрик . Вот я и того... через условную компиляцию два варианта класса завел и по нужде один из них прикомпиливаю.
1. Самое то!
2. Не обязательно условную компиляцию, можно интерфейсы, типа такого:
....
В принципе я понял. Только не знаю - имеет ли смысл держать два жирных драйвера в мелком МК, если учесть тот факт, что устройства на ходу интерфейсные модули не меняют.
...и, вроде, у них еще и какая-то междусобойная непереносимость - начинают массово ошибки компиляции валиться, если их одновременно подключать (Ethernet и UIPEthernet). А разбираться в чужом коде уровня драйвера на ENC28J60 мне не по зубам. Так что пошел простым путем.
Из всего обсуждения я понял, что выбрал не самый худший способ и потери по памяти интуитивно (т.е. наугад) можно оценить в десяток указателей.
Да все, кто отписался, что не использует goto - ещё как используют, просто в неявном виде. Каждый if, switch/case, return - это всё goto (jz je jmp).
Отнюдь.
if, switch/case, return - это структурные операторы, а goto - это бесструктурный. На одной и той же бумаге можно написать как нетленку, так и порнографию, но это же не повод, чтобы сравнивать.
НО! Немногие знают, что в Delphi есть структуры, практически аналогичные классам C++ (за исключением ряда нюансов). Это объекты (object). Этот тип достался по наследству от Turbo Pascal, и до сих пор поддерживается в современных версиях Delphi. Есть наследование, конструкторы/деструкторы, простые и виртуальные методы. Память для экземпляра может выделяться как статически, так и динамически (классы Delphi - только динамически).
Я за object знаю еще со времен Turbo Vision. Действительно, это тип - значение, как и record. Но, я говорил про то, что в Delphi нельзя наследовать класс от record, и, если уж на то пошло, от object тоже. Только класс от класса, Object от object-а, а record вапще не наследуется. Казёл.
Я за object знаю еще со времен Turbo Vision. Действительно, это тип - значение, как и record. Но, я говорил про то, что в Delphi нельзя наследовать класс от record, и, если уж на то пошло, от object тоже. Только класс от класса, Object от object-а, а record вапще не наследуется. Казёл.
Ну, то, что record не наследуется, это, конечно, неудобно. Но в целом отсутствие перекрестного наследования - вполне в стиле Паскаля, что, в общем-то, довольно логично.
В целом, в Си-шную структуру такие указатели на функции впендюриваются "на раз" и в общем-то необходимость в виртуальных методах отпадает или становится не значительной. А без виртуальности С++, как ЕвгенийП уже замечал ранее - это "Си с классами", которые .. ага, по компактности кода практически равны "С со структурами". :)
Верно излагаеш. Добавлю тока что активная работа с указателями на функции - офигенно мощная штука. Даже самое мощное что есть в чистых сях. Её использовоние покрывает всю виртуальность плюс еще много разного интересного дает. И походу это самое опасное. Неаккуратность валит програму как два пальца. Вот потому вокруг него и накрутили ООП. Чтоб с одной стороны заюзать неопытными пользователями, заменив им понимание работы процессора на догмы ООП, а с другой стороны спрятав указатели в VMT более-менее обезопасить технологию. Ну а для пипла, чтоб прикрыть, что осетра им урезали, служат рассказы про более высокий уровень абстрагирования и строгое разграничение. Что и слышим. Абстрагирование - оно в голове и от неё зависит, а разграничение на модули и либки намного строже получается чем на классы.
Про экономию ресурсов и указатели на функции. Классическая машинка состояния, свитч, многоэтажные кейсы... Загоняем кейсы в функции, а их указатели в массив в PROGMEM и вместо длинючего свитча по некоторому id получаем нечто типа ArrFunc[id](). А если лямбда-функции использовать, то и лишних имен придумывать не прийдется.
А мне вааще Си - нравится с того самого первого моменту, когда произошла эта чудесная встреча .. вплоть до момента, когда пришло осознание что на нем можно ваять (ну почти) всё и практически так как захотел на так любимом мною прежде Ассемблере .. нельзя только одну мелочь: прямая работа с флагом переноса и рядом других флагов. Вот нарисовать на Си арифметику с переносом (скажем трехбайтовых целых) - не получится.
Точно такжеж и тут: посмотрев и освоив С++ в свое время .. внезапно понял, что ничего, акромя "синтаксического сахара" в нем нет. Но, для новичков пугать ООП оказалось очень даже полезно... потом за 17-и лдетним перерывом оно забылось и всплыло совсем не так давно. В смысле понимание, что на Си можно писать практически ВСЁ и как на "жабаскрипт" и как ООП и много лучше. :)
(* ещё бы препроцессор допилить на предмет некоторых упущений, но и без них неплохо *)
По поводу экономии ресурсов: никакой "уровень абстракции вверх" не позволяет этого делать. А вот "внятно" (и быстро) можно писать на чем угодно - дело привычки и культуры писателя. :)
DIYMAN, просвети, реально в С++ можно наследовать класс от структуры?
А они ничем не отличаются ;) Структура - это то же самое, что
В структуре модификатор доступа по умолчанию ко всем членам - public. Но ничего не мешает и там разделить на private, protected и public. Короче, ключевое слово struct в C++ - это класс со всеми открытыми членами по умолчанию. Я просто юзаю их для экономия кол-ва нажатий клавиш :), когда это необходимо.
Да, спасибо, мне Евгений Петрович уже разъяснил. Век живи, век учись. :)
Кстати, о goto, я тут как-то уже писал о таком замечательном языке, как INTERCAL. В нём нет goto (от слова вообще). Зато там есть противоположный оператор - comefrom (получить управление из) - замечательная вещь!
Наследовать классы (class) от структур (record) в Delphi нельзя, потому что эти типы принципиально разные, не так как в C++.
НО! Немногие знают, что в Delphi есть структуры, практически аналогичные классам C++ (за исключением ряда нюансов). Это объекты (object). Этот тип достался по наследству от Turbo Pascal, и до сих пор поддерживается в современных версиях Delphi. Есть наследование, конструкторы/деструкторы, простые и виртуальные методы. Память для экземпляра может выделяться как статически, так и динамически (классы Delphi - только динамически).
Практическая польза от object в современном коде может быть там, где требуется структурный тип, который можно наложить на любую память, но более развитый, чем record. В частности, в структурах данных при реализации протоколов удобно применять наследование:
Отсюда и вопрос: выбор класса, как некоего враппера - это выбор нормальный или есть получше?
Задача у меня, конечно, позаковыристей: есть два драйвера, немного отличаются методами. Надо было сделать так, чтобы из основного кода дергать всё однотипно и держать некоторое кол-во метрик . Вот я и того... через условную компиляцию два варианта класса завел и по нужде один из них прикомпиливаю.
1. Самое то!
2. Не обязательно условную компиляцию, можно интерфейсы, типа такого:
....
В принципе я понял. Только не знаю - имеет ли смысл держать два жирных драйвера в мелком МК, если учесть тот факт, что устройства на ходу интерфейсные модули не меняют.
...и, вроде, у них еще и какая-то междусобойная непереносимость - начинают массово ошибки компиляции валиться, если их одновременно подключать (Ethernet и UIPEthernet). А разбираться в чужом коде уровня драйвера на ENC28J60 мне не по зубам. Так что пошел простым путем.
Из всего обсуждения я понял, что выбрал не самый худший способ и потери по памяти интуитивно (т.е. наугад) можно оценить в десяток указателей.
Странно это.
Я вот уже около четверти века не использую GOTO. Совсем.
И еще ни разу не понадобилось.
Ни разу не использовал с момента начала занятий программированием. Ни разу не понадобилось :)
Так до этого я 15 лет писал на Фортране!
И, кстати, с Фортраном до сих пор не распрощался. Но уже без GOTO.
Да все, кто отписался, что не использует goto - ещё как используют, просто в неявном виде. Каждый if, switch/case, return - это всё goto (jz je jmp).
Отнюдь.
if, switch/case, return - это структурные операторы, а goto - это бесструктурный. На одной и той же бумаге можно написать как нетленку, так и порнографию, но это же не повод, чтобы сравнивать.
Ну я всегда знал, что интеллектом не блещу...
Интеллект - он не для блеска. А шоб был.
НО! Немногие знают, что в Delphi есть структуры, практически аналогичные классам C++ (за исключением ряда нюансов). Это объекты (object). Этот тип достался по наследству от Turbo Pascal, и до сих пор поддерживается в современных версиях Delphi. Есть наследование, конструкторы/деструкторы, простые и виртуальные методы. Память для экземпляра может выделяться как статически, так и динамически (классы Delphi - только динамически).
Я за object знаю еще со времен Turbo Vision. Действительно, это тип - значение, как и record. Но, я говорил про то, что в Delphi нельзя наследовать класс от record, и, если уж на то пошло, от object тоже. Только класс от класса, Object от object-а, а record вапще не наследуется. Казёл.
Интеллект - он не для блеска. А шоб был.
У меня теперь нету.
Я за object знаю еще со времен Turbo Vision. Действительно, это тип - значение, как и record. Но, я говорил про то, что в Delphi нельзя наследовать класс от record, и, если уж на то пошло, от object тоже. Только класс от класса, Object от object-а, а record вапще не наследуется. Казёл.
Ну, то, что record не наследуется, это, конечно, неудобно. Но в целом отсутствие перекрестного наследования - вполне в стиле Паскаля, что, в общем-то, довольно логично.