1.1. Отсутствие перегрузки
функций. Это большое зло. Это надо, это сокращает время для написания
кода. Плюс этот недостаток порождает целый ряд других. Пример: Нужен
менеджер ресурсов. Там нужен такой метод LoadRes(target:TypeOfResource
var, url$). Вот при перегрузке функций, нужная функция в зависимости от
типа нужного ресурса будет подставляться на этапе сборки. А без
перегрузки я должен: первое - писать свой обработчик по типу ресурса
(если сделать одну функцию с параметром типа общего объекта - Object) -
тратить свое время на лишний код! И второе - этот код будет выполняться
во время работы процесса, что будет снижать быстродействие! Уже два
ОГРОМЕННЫХ МИНУСА! Можно обойти это немного, конкретизировав методы
загрузки ( LoadImage(url$), LoadSound(url$) ), но это немного снизит
удобство. Т.к. для каждого ресурса надо будет помнить свою функцию.
1.2.
Отсутствие конструкторов с параметрами. Писать функции для создания
классов - не очень то и удобно с точки зрения поддержки и развития
кода. - Их ведь нельзя наследовать и п1 - нельзя перегружать. Делать
для инициализации статические методы (тоже что и функции по сути) при
наличии потомков - тоже неудачно и упирается в отсутствие перегрузки
функций и методов - см.сл.п.
1.3. Неправильная или глючная
поддержка в потомках возвращаемых значений методов. Т.е. в потомке
нельзя переопределить тип возвращаемого значения. Оно логично со
второго раза, а с третьего - видно, что не менее логично, если тип
возвращаемого значения - объект, то в методе потомка можно возвращать
потомка от исходного значения-объекта. Чем плохо конкретно - тем, что
нельзя писать одноименные методы-конструкторы для потомков и предков
красиво и без извратов (приведения типов, что делает код громоздким и
невыразительным). А так же методы копирования. Т.е., по сути, нет
перегрузки методов (и функций), что неудобно.
1.4. Невозможность
создания указателей на типы пользователя. Например я сделал тип
TRGBA_Color с полями R, G, В, и А соответсвенно для цветов и
прозрачности. Сделать так я не могу: Local p:TRGBA_Color Ptr = TRGBA_Color Ptr(bmp.PixelPtr(0,0)) Т.е.
получить указатель на пикселы и работать с ним красиво - p.R = 100.
Приходится делать это указателем на байты, что с ЯП высокого уровня не
очень то и ассоциируется. Похоже, это связано с тем, что каждый тип
является объектом и переменная этого типа уже и есть указатель на
объект и заботой о защите памяти (только safe - функционал). Приведение
типов или индексный доступ, конечно, не защищены от "дурака" -
прогламмера (unsafe - функционал), но позволяют "не дураку" нормально и
удобно работать.
1.5. Нет команды SinCos, которая поддерживается
новыми процессорами и позволяет быстрее производить операции просчета
вращения и т.п., т.к. обычно надо и синус и косинус угла, а одной
командой это явно сделать быстрее, чем двумя.
1.6.Статический
step в циклах. Иногда надо, чтобы при проходе по массиву приращение
индекса менялось. Приходится делать через while/wend.
1.7. Нет оператора With. Можно сделать аналог, но это увеличивает громоздкость кода.
1.8.
Система слежения за освободившейся памятью (менеджер памяти языка) и
автоматическим удалением не нужных уже объектов, хоть и хороша и
удобна, но не совершенна. И порой возникали утечки из-за перекрестных
ссылок (кросслинков). Т.е. все равно приходится следить за корректной
очисткой памяти, что отвлекает от основного процесса написания кода,
увеличивает объем текста программы и в сумме эти два фактора приводят к
увеличению времени написания программы. Плюс это не характерно для ЯП
высокого уровня - менеджер памяти должен быть на высоте. Так что это
получается что-то среднее между ЯП высокого и среднего уровней. Утечки
возникают даже не при кросс-линках а и в ситуациях, когда возникать не
должны. Например: объект-контейнер, содержит в себе список других
объектов. Объекты содержат поле с линком на элемент списка типа TLink,
- не на сам контейнер. На этот контейнер ссылается глобальная
переменная, поэтому он и "живет". Глюк: если в методе объекта из списка
контейнера глобальная переменная переопределяется (в "пусто" или другой
контейнер), то память не очищается! Контейнер не удаляется! При
освобождении объекта в другом месте - все нормально и память
освобождается. Глюк лечится, если объект из контейнера при
переопределении глобальной переменной выбрасывается из списка -
Tlink.Remove(). Но вообще-то такого быть не должно.
1.9. Нет
указателей на методы типов. Указатели на функции - есть, а на методы -
нет. А это облегчило бы написание кода в некоторых случаях.
1.10.
Нет нормальной полноценной совместимости (приведения) типов.
Неоднократно сталкивался с тем, что надо было массив TObject[]
преобразовать к моему типу (например для возврата из функции, для
конвертирования из TList к массиву). Приходилось писать свои функции
для преобразования вроде ObjArrayToTMytypeArray: TMytype[]( Object[] ).
Можно в принципе и без этого - приводить типы уже во время работы с
массивом, но наглядность и легкость чтения кода теряются. А это надо
для реюзабельности кода. П.с.: Таки сделали приведение типов для массивов - хорошо хоть развивается язык, а не стоит на месте. П.п.с.: Млин. Криво работает. Нулевые массивы на выходе часто.
1.11. Нет директивы inline. Нужна она! И не только для ускорения работы кода.
1.12. Глюк с продолжением строки: если второй строкой идет комментарий, то выдается ошибка: Field pms:TPixmap[] = [ .. 'LoadImage("images/gem_1_rotate.png") .. - это нужно для комментирования при экспериментах с различными значениями в массиве.
1.13.
Оптимизация кода очень плохая. Нет оптимизации по использованию методов
- методы, которые ни разу не использовались в коде, все равно
компилируются. Тот код, который пишется, буквально переводится один в
один.
1.14.
Нет инкапсуляции, т.е. защищенных и открытых методов и полей класса.
Есть ее какое-то подобие на уровне модуля, но не в классе. А отсутствие
инкапсуляции - это есть unsafe.
1.15.
Нет автоматического приведения типов. Т.е. если в функции объявлен один
тип, а передается другой, причем совместимый (общий класс Object),
возникает ошибка.
1.16.
Игры с типами не доделаны. Вот начал я привыкать к этим играм с
типизацией и ощутил необходимость ссылки на тип. То, что в С++
шаблонами можно сделать. Такая переменная в которой нужный тип
обозначен - очень нужно для наследования и для различных фабрик
объектов, чтоб кода писать меньше. А то можно и приустать конструкторы
под каждый новый тип переписывать, раз уж нет их с параметрами и
перегрузки функций тоже нет. П.с.: !!! Я придумал, как это обойти. В
нужных нам классах метод Copy(), создающий копию класса. Неужели
сделали перегрузку хоть на уровне преобразования возвращаемого
результата с учетом наследования. Но - нужно самому писать весь код по
копированию. С версии 1.26 введен механизм отражений, который ввел тип TTypeId, который и является описанием типа или ссылкой на тип.
1.17. Нет автоматических конструкторов копирования объектов. Приходится писать самому - а это время. А время - деньги.
1.18.
Несколько глупо реализовано конструирование объектов при наследовании.
Переопределенный конструктор предка вызывается по любому, даже если мы
его не вызываем и его вызов нам не нужен, т.к. мы переопределяем
конструктор в наследнике. Практический пример: нужно незначительное изменение предка. Предок использует поле объекта одного типа, в
наследнике нужно это же поле, но другого типа. В конструкторе этот
объект создается. В наследнике значит надо переопределить создание
этого объекта на нужный нам тип. Но в конструкторе предка этот объект
тоже создается, а потом пересоздается в наследнике. Конечно, менеджер
памяти лишний объект потом удалит, но вообще получается очень глупое и
неразумное использование ресурсов. Хорошо если этот объект маленький, а
если требует серьезных расчетов, подгрузки ресурсов и т.д. - получится
заметное снижение производительности. Поэтому предок надо писать заново
либо с нуля, либо с предка предка, еще не имеющего нужных нам полей
объектов. Вывод: Язык не очень дружелюбен к наследованию и,
следовательно, к хорошей поддержке крупных и долгосрочных проектов.
1.19. Нет макросов и шаблонов. Хотя бы макросы блин – это
уже несколько заменило бы шаблоны. А нужно это чтобы не писать кучу однотипного
кода с небольшими вариациями. Это бы прилично сократило время на набор текстов
программ. 1.20.Переопределение полей в наследниках. Считаю это небезопастным свойством языка. Дело в том, что не выдается никаких сообщений при компиляции по поводу
переопределения полей. И можно при последующей доработке класса,
нечаянно переопределить какое-либо существующее поле, что приведет к
ошибкам в работе логики программы. И уж если нельзя делать перегрузку и переопределение методов в наследниках, то, почему это можно в отношении полей? - Нарушается внутренняя логика языка.
1.21. Компилятор и конечный код - просто ужасны. Я молчу про оптимизацию, элементарные вещи делаются через объездые пути. Пример: Простейшая функция: function _RDT:long() return 111 endfunction
Сгенерированный Blitzmax код: push ebp mov ebp,esp sub esp,8 mov edx,dword [ebp+8] mov dword [ebp-8],111 mov dword [ebp-4],0 _3: mov eax,dword [ebp-8] mov dword [edx],eax mov eax,dword [ebp-4] mov dword [edx+4],eax mov esp,ebp pop ebp ret
Это же просто ужас. Зачем метка внутри? - она не используется. Зачем отнимается от указателя стека 8 в начале? Что за манипуляции с ним между BP и DX? И вот аналогичный нормальный код, выполняющий ту-же задачу: push ebp mov ebp,dword[esp+8] mov dword [ebp],111 mov dword [ebp+4],0 pop ebp ret
Ну что тут можно написать? - Отстой! (А что вы хотели за 10 копеек всего? Юнити - тру!)
1.22. Нет поддержки событий. События - это хорошо! Их поддержка быть должна. Можно конечно и без них и даже с ними, если самому либу написать. Но в правильных языках это есть. Это кстати пересекается с п 1.9 - Нет указателей на методы. Чтоб нормально работать с событиями - они должны быть. И с нормальным синтаксисом, а не как в С++. Можно, конечно, через отражения сделать - но это ж как неэффективно, плюс ошибки во время работы, а не на этапе компиляции - нехорошо это. Однозначно!
Другие материалы по теме
Источник: http://bmax-ru.blogspot.com/2007/06/blitzmax-1.html |