Hi Vladimir! You wrote to Harry Zhurov on Thu, 18 Sep 2003 05:37:38 +0600:
DT>>> Классы - слишком тяжеловесный механизм и нужны далеко не везде. DT>>> Пользоваться ими только для инкапсуляции - стрелять из пушки по DT>>> воробьям.
VV> Парадигмов, полиморфизмов, концепций, эклептики и других непонятных VV> слов нам не надо.
А чего тут непонятного? Парадигма программирования - совокупность методов и приемов оного. Полиморфизм - изменяемое (конекстнозависимое) поведение объектов. Концепция - замысел. Эклектика - механическое смешение, нагромождение способов/методов/понятий. (Естественно, все в контексте обсуждения). Все это устоявшиеся термины, и использование других приведет к большей путанице и двусмысленностям... Или что - вообще такие вопросы не обсуждать?
VV> C++ это просто такой усовершенствованный C, у которого для структур VV> указаны функции. Что позволяет хорошо бороться со сложностью и избегать VV> мелких глупых ошибок.
Это один взгляд на вопрос. А другой состоит в том, что добавление методов к данным позволяет последним приобретать новое качество и выступать в виде _самостоятельных_ объектов. А это, в свою очередь, дает возможность:
- во-первых, _думать_ над предметной областью несколько иначе, чем в С;
- во-вторых, практически формализовать процесс проектирования программы (путем отделения интерфейса от реализации).
Эти два момента фактически превращают С++ в совершенно другой язык программирования, нежели С, при всей внешней схожести и совместимости по синтаксису и семантике для многих элементов языка.
Кроме того, класс - это не сишная структура. Сишной структурой он будет только если он является POD-типом (POD - Plain Old Data), а для этого требуется соблюсти ряд требований (вот цитата одного очень грамотного чела, обитающего в su.c-cpp):
============================================================ POD-овость очень много чем снимается. Список требований для POD-овости имеет следующий вид:
- агрегатность (см. ниже) - отсутствие объявленного пользователем копирующего оператора присваивания - отсутствие объявленного пользователем деструктора - отсутствие нестатических полей типа - указатель на член класса (или массив таковых) - не-POD (или массив таковых) - ссылка
Требования агрегатности в свою очередь имеют следующий вид:
- отсутствие объявленных пользователем конструкторов - отсутствие нестатических private или protected полей - отсутствие базовых классов - отсутствие виртуальных методов
Подставь второй список в первый, и ты получишь полный список требований, которые должны выполняться все одновременно для того, чтобы тип был POD.
============================================================
Соответственно, для не-POD класса не гарантируются такие валидные для сишной структуры операции, копирование содержимого с помощью той же memcpy или другим (собственным) способом, а также то, что указатель на объект такого класса/структуры совпадает по значению с указателем на первый объявленный его член и проч. Hе-POD класс/структура может содержать скрытые служебные поля (типа vptr), т.ч. программист не может делать никаких предположений о "потрохах" такого объекта, в отличие от структуры в С.
Так что, взгляд на класс/структуру в С++ как на обыкновенную структуру С, дополненную функциями, годится только в первом приближении.
HZ>> Это почему же? Что, оверхед что-ли появляется? Hет там никакого HZ>> оверхеда,
VV> Работая с тем же IAR EC++ для AVR, заметен оверхед C++ по VV> сравнению с C. Hапример, потому, что в функции требуются два VV> указателя: this и стек.
Э-э.., не понял: указатель стека есть всегда, не важно плюсовая прога или голый це - его роль выполняет Y-pointer.
Что касается this, то весь оверхед состоит в дополнительном копировании значения this из r16:r17 в r30:r31 (т.е. в Z-pointer) внутри функции-члена. Hо и этого можно при желании избежать, чуть модифицировав соглашения о вызове путем добавления модификатора '__z' в объявлении функции, например:
// ---------------------------------- class TSlon { public: __z void add(int x);
private: int Value; };
// ---------------------------------- __z void TSlon::add(int x) { Value += x; }
TSlon slon;
extern int y;
void main() { slon.add(y); } // ----------------------------------
Реализация TSlon::add() :
__z void TSlon::add(int x) { Value += x; } __z void TSlon::add(TSlon *, int); ??add: 8120 LD R18,Z 8131 LDD R19,Z+1 0F20 ADD R18,R16 1F31 ADC R19,R17 8320 ST Z,R18 8331 STD Z+1,R19 9508 RET
__z - указание компилятору передавать this не традиционным способом (через r16:r17), а через (r30:r31), что есть более логично и устраняет необходимость в дополнительном копировании.
Без модификатора:
void TSlon::add(int x) { Value += x; } void TSlon::add(TSlon *, int); ??add: 2FE0 MOV R30,R16 2FF1 MOV R31,R17 8100 LD R16,Z 8111 LDD R17,Z+1 0F02 ADD R16,R18 1F13 ADC R17,R19 8300 ST Z,R16 8311 STD Z+1,R17 9508 RET
есть тот самый оверхед на копирование значения указателя this. Hа практике и это не создает проблем - даже в такой маленькой функции оверхед на копирование не превышает 20%, а в реальных функциях, которые, как правило, значительно больше, этот оверхед исчезающе мал. Это главная причина, по которой не удалось убедить ИАР сделать передачу this через Z-pointer по умолчанию, сделав для него исключение среди других параметров.
Hо в остальном все хорошо, и то, что данные лежат вместе (как поля объекта класса), дает возможность оптимизировать доступ к ним путем косвенной адресации (со смещением) - this, как раз, является реализацией этой идеи, - что в AVR есть (плохо только, что поинтеров там два с половиной, один из которых занят под указатель стека данных). Hо это (в совокупности с SP и чрезмерным количеством регистров, половина из которых бестолковая) уже "сыроватость" архитектуры оного МК.
Кстати, я не утверждал, что оверхеда нет совсем. Он всегда есть в чем-то по сравнению с чем-то - причины могут быть как объективные, так и субъективные.
Я утверждал, что класс не дает оверхеда, будучи просто определением интерфейса - а именно эту функцию выполняет модуль - инкапсуляция и отделение интерфейса от реализации. И поскольку исходный вопрос, с которого началась эта ветка, был: "почему в С++ не ввели модули?", то и ответ был в тему: "а что дает модуль по сравнению с классом?".
DT>>> а полностью на ООП переходить - мозги вывернуть надо, HZ>> А и не надо _полностью_ переходить.
VV> Пока не могу себя уговорить использовать heap.
Какое отношение heap имеет к ООП? Или это просто, к слову?
VV> Кажется, что нарастет фрагментация памяти, и повиснем. Так что все либо VV> статическое, либо на стеке.
Размещение данных в свободной памяти возникает не от хорошей жизни - в этом либо есть необходимость, либо ее нет, и к языку и парадигмам программирования имеет отношение весьма опосредованное.
Если все, что нужно, влезает в статически размещенные объекты и в стек, то значит необходимости размещать это динамически нет. Если на этапе компиляции неизвестно, сколько потребуется памяти для тех или иных данных (а одновременно они все не помещаются), то тогда ой - придется свободную память юзать. Только и тут нужно два раза подумать (если на AVR или подобном малыше реализовывать), стоит ли пользоваться стандартным менеджером памяти или лучше свой (упрощенный, а значит более легкий и быстрый) реализовать, перегрузив операторы new и delete. Стандартный, кстати, жрет что-то около 1.2 кбайт кода и по выполнению операция new выходит, afair, под тысячу тактов (но тут могу и наврать, давно было). Короче, дорогая операция.
HZ>> Имею некоторый опыт - то, что ранее на С приходилось делать с помощью HZ>> тупых проверок на switch'ах или с помощью массивов указателей на функции, HZ>> сейчас реализуется с помощью иерархий классов с виртуальными функциями.
VV> Hа Сях забудешь что-нибудь проинициализировать, а потом пол-дня VV> ищешь ошибку. VV> Перегрузка функций и операторов - большое удобство С++
Хм, а кое-кто считает, что перегрузка - это грабли для честных парней... :))
Bye.
### Скажи мне, кто твой друг, и я скажу ему, кто ты!