Мой сайт
Главная | | Регистрация | Вход
Приветствую Вас Гость | RSS
Меню сайта
Наш опрос
Оцените мой сайт
Всего ответов: 6
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0
Форма входа
Поиск
Календарь
«  Июнь 2013  »
Пн Вт Ср Чт Пт Сб Вс
     12
3456789
10111213141516
17181920212223
24252627282930
Архив записей
Друзья сайта
  • Официальный блог
  • Сообщество uCoz
  • FAQ по системе
  • Инструкции для uCoz
  • Главная » 2013 » Июнь » 23 » "Клуб программистов" – материалы по Delphi и С++
    15:55
     

    "Клуб программистов" – материалы по Delphi и С++

    Posted by bullvinkle under Журнал, Статьи

    Задача предназначена для представления краткого обзора нововведений в язык Дельфи (2010) по сравнению с Дельфи 7.

    Изменения в языке Дельфи 2010

    Utkin

    Благодаря активным попыткам компании Embecadero влиять на рынок продуктов разработки программ язык Дельфи быстро развивается, однако это развитие направлено в основном на попытки наверстать все нововведения в современных языках программирования (таких как С#). Никаких принципиально новых разработок и концепций не внедряется.

    Директива Inline (появилась в Дельфи 2005)

    По аналогии с С++, функции и процедуры теперь могут быть встраиваемыми со всеми вытекающими последствиями. А именно использование данной директивы не гарантирует вставку тела функции вместо ее вызова. Кроме того, существует еще целый ряд ограничений (согласно справочной системе). Эта директива бесполезна:

    . при позднем связывании (virtual, dynamic, message);

    . для функций и процедур имеющих ассемблерные вставки;

    . для конструкторов и деструкторов, там она работать не будет (о чем Вам компилятор обязательно пожалуется);

    . для главного блока программы, секций инициализации и финализации модулей;

    . если метод класса обращается к членам класса с более низкой видимостью, чем сам метод. Например, если public метод обращается к private методу, то для такого метода inline-подстановка осуществляться не будет;

    . для процедур и функций, которые используются в выражениях проверки условия циклов while и repeat.

    Как сделать процедуру встроенной?

    Procedure Add (var x: Integer; y: Integer); Inline;

    Регулировать поведение inline можно следующими директивами:

    {$INLINE ON} – по умолчанию включена, разрешает работу Inline;

    {$INLINE AUTO} – будет осуществлена попытка встраивание кода функций и процедур, если:

    а) они помечены как Inline;

    б) если их размер будет менее 32-х байт.

    {$INLINE OFF} – не разрешает работу Inline.

    Следует отметить, что и в классическом С++ Inline никогда не была высокоэффективным механизмом, а учитывая ограничения, накладываемые компилятором Дельфи, ее использование под большим вопросом.

    Перегрузка операторов (появилась в Delphi.Net)

    В отличие от С++ перегрузка осуществляется немного по-другому. Для перегрузки операторов перегружается не символ оператора, а его символическое обозначение (сигнатура). Перегружать можно только для операций с экземплярами классов.

    table

    Нужно обратить внимание – TRUNC, ROUND, INC, DEC считаются операторами, а не процедурами и функциями.

    Вот пример использования:

    TMyClass = class

    class operator Add(a, b: TMyClass): TMyClass; // Перегрузка сложение для TMyClass

    class operator Subtract(a, b: TMyClass): TMyclass; // Вычитание для TMyClass

    class operator Implicit(a: Integer): TMyClass; // Неявное преобразование Integer в TMyClass

    class operator Implicit(a: TMyClass): Integer; // Неявное преобразование TMyClass в Integer

    class operator Explicit(a: Double): TMyClass; // Явное преобразование Double в TMyClass

    end;

    // Пример описание сигнатуры Add для перегрузки сложения для типа TMyClass

    TMyClass.Add(a, b: TMyClass): TMyClass;

    begin

    end;

    var

    x, y: TMyClassbegin

    x := 12; // Неявное преобразование из Integer

    y := x + x; // Вызов TMyClass.Add(a, b: TMyClass): TMyClass

    b := b + 100; // Вызов TMyClass.Add(b, TMyClass.Implicit(100))

    end;

    Подробней о перегрузке операторов можно почитать здесь: http://www.realcoding.net/articles/delphinet-peregruzka-operatorov.html

    Помощники класса (Class Helpers)

    Интересный механизм (ответ Дельфи на расширители классов в С#), призванный решить некоторые проблемы в обход наследования. Служит для дополнения класса новыми методами и свойствами.

    type

    TMyClass = class

    procedure MyProc;

    function MyFunc: Integer;

    end;

    procedure TMyClass.MyProc;

    var

    X: Integer;

    begin

    X := MyFunc;

    end;

    function TMyClass.MyFunc: Integer;

    begin

    end;

    type

    TMyClassHelper = class helper for TMyClass

    procedure HelloWorld;

    function MyFunc: Integer;

    end;

    procedure TMyClassHelper.HelloWorld;

    begin

    WriteLn(Self.ClassName); // Здесь будет возвращен тип TMyClass, а не TMyClassHelper

    end;

    function TMyClassHelper.MyFunc: Integer;

    begin

    end;

    var

    X: TMyClass;

    Begin

    X := TMyClass.Create;

    X.MyProc; // Вызов TMyClass.MyProc

    X.HelloWorld; // Вызов TMyClassHelper.HelloWorld

    X.MyFunc; // Вызов TMyClassHelper.MyFunc

    end;

    По сути, вариация на тему множественного наследования, но есть одна особенность – помощники класса позволяют дополнять любой существующий класс, без создания нового. Обратите внимание, что механизм помощника класса не использует явного упоминания Self при обращении к полям класса (помогаемого класса). То есть, HelloWorld имеет право обращаться к полям TMyClass (просто в нашем примере их нет). Аналогично TMyClass также имеет доступ к полям TMyClassHelper (в случае, если класс и его помощник объявлены в одном модуле).

    С практической точки зрения удобный механизм, кроме одной детали – класс должен иметь только одного помощника, имеет ли он помощника проверить во время выполнения программы нельзя. Если в классе имеется несколько помощников (неважно в каком юните, лишь бы он видел класс), считаться рабочим будет только самый последний из объявленных. Это значит, что если TMyClass уже имел помощника, то будут доступны методы именно TMyClassHelper, поскольку именно он объявлен последним. Таким образом, в лучшем случае, два и более помощника для одного класса вызовут ошибку компиляции, в худшем трудно отлавливаемую ошибку, жалобы программиста на багги в IDE и компиляторе и много потерянного времени. Чем сложней проект, тем трудней будет установить причину ошибки.

    С теоретической точки зрения механизм противоречивый – он увеличивает сцепляемость объектов и юнитов между собой. Перед использованием помощника, я должен проверить все модули, из которых доступен данный класс на предмет проверки существования такого помощника (представьте большой проект). Это нарушает принципы инкапсуляции – если раньше перед использованием класса нужно было знать только его интерфейс, то теперь для использования помощников, я должен отслеживать существование класса во всех модулях, где имеется доступ к данному классу. С этого момента механизм интерфейсов уже не играет особой роли, поскольку, обращаясь к объекту какого-либо класса, всегда можно обнаружить такой букет неизвестных методов, что интерфейсная часть класса становится даже вредной. Это нарушает принцип сокрытия данных – благодаря помощникам я могу менять работу своих и чужих классов и могу иметь доступ к его полям (в рамках юнита). Кстати, это ограничение на доступ к полям в рамках юнита также сводит на нет многие его плюсы – проще вписать новые методы в сам класс (или наследовать новый), чем создавать путаницу в классе, юните и проекте.

    Записи стали объектами

    И теперь имеют свои методы, свойства и конструкторы.

    type

    TMyRecord = record

    type

    TInnerColorType = Integer;

    var

    Red: Integer;

    class var

    Blue: Integer;

    procedure printRed();

    constructor Create(val: Integer);

    property RedProperty: TInnerColorType read Red write Red;

    class property BlueProp: TInnerColorType read Blue write Blue;

    end;

    constructor TMyRecord.Create(val: Integer);

    begin

    Red := val;

    end;

    procedure TMyRecord.printRed;

    begin

    writeln(’Red: ‘, Red);

    end;

    Но, сокращенная запись по-прежнему разрешена (поэтому старые проекты должны переноситься и с сокращенной формой записей).

    Абстрактные классы

    type

    TAbstractClass = class abstract

    procedure SomeProcedure;

    end;

    Разрешены полностью абстрактные классы (раньше допускались только конкретные методы), содержащие объявления методов для дальнейшего их перекрытия в потомках.

    strict private и strict protected

    Строгое private – метод или свойство для класса и невидимое никому, вне класса даже в рамках текущего юнита.

    Строгое protected – методы в этой секции будут видимы самому классу и его наследникам.

    Таким образом, полное объявление выглядит теперь так

    type

    TKlass = class(TForm)

    strict protected

    protected

    strict private

    private

    public

    published

    automated

    end;

    Не наследуемые классы

    По аналогии с С#, в Дельфи 2010 существуют классы от которых дальнейшее наследование невозможно:

    type

    TAbstractClass = class sealed

    procedure SomeProcedure;

    end;

    Весьма сомнительное удовольствие для рядового разработчика. Никаких реальных преимуществ такой класс не дает. Точные причины создания такого механизма не известны и преимущества от его использования очень призрачны – наследование не разрушает класса предка, поэтому и запечатывать их особой необходимости нет. Считается, что запечатанные классы работают быстрей обычных (сам не проверял) и они применяются для .NET платформы (сугубо в утилитарных целях – не все обертки над низкоуровневыми операциями, такими как WinApi, можно сделать наследуемыми).

    Классовые константы (возникло в Delphi 8)

    Классы могут иметь константы – сами классы, а не порождаемые от них объекты.

    type

    TClassWithConstant = class

    public

    const SomeConst = ‘This is a class constant’;

    end;

    procedure TForm1.FormCreate(Sender: TObject);

    begin

    ShowMessage(TClassWithConstant.SomeConst);

    end;

    Классовые типы (возникло в Delphi 8)

    Класс может теперь содержать описание типа, которое можно использовать только в пределах данного класса.

    type

    TClassWithClassType = class

    private

    type

    TRecordWithinAClass = record

    SomeField: string;

    end;

    public

    class var

    RecordWithinAClass: TRecordWithinAClass;

    end;

    procedure TForm1.FormCreate(Sender: TObject);

    begin

    TClassWithClassType.RecordWithinAClass.SomeField := ‘This is a field of a class type declaration’;

    ShowMessage(TClassWithClassType.RecordWithinAClass.SomeField);

    end;

    Еще одно сомнительное удовольствие. Описание типа это не конкретная структура, зачем прятать его описание в тело класса?

    Классовые переменные (возникло в Delphi 8)

    Класс может содержать переменные по аналогии с константами:

    Type

    X = class (TObject)

    Public

    Var

    Y: Integer;

    End;

    Пожалуй, единственное, где это может пригодиться это работа с RTTI, вообще классы в Дельфи стали больше напоминать юниты – такие вот юниты внутри юнитов. Обратите внимание, что переменные класса могут находиться в любой секции (секции в данном случае влияют на область видимости данных переменных), тогда как поля класса не могут быть public (в Дельфи 7 могли). Применение статических полей в классе делает Дельфи все более ориентированным в сторону С# (и менее в сторону Паскаля).

    Вложенные классы

    Теперь классы можно объявлять внутри классов, цель избежать конфликта имен, локализовать все связанные классы между собой:

    type

    TOuterClass = class

    strict private

    MyField: Integer;

    public

    type

    TInnerClass = class

    public

    MyInnerField: Integer;

    procedure InnerProc;

    end;

    procedure OuterProc;

    end;

    procedure TOuterClass.TInnerClass.InnerProc;

    begin

    end;

    Классы все больше перенимают концепцию модулей. Понятно, что данное нововведение дань .Net, но реальной пользы от него опять же не очень много – раньше конфликта имен избегали префиксами A и F, не могу сказать, что новый механизм дал программистам новые возможности. Также как и попытка использовать вложенные классы для складывания всего в одну большую кучу наряду с помощниками классов сильно напоминают лебедь, рак и щуку, растаскивающие Дельфи в разные стороны.

    Финальные методы класса

    В классах можно создавать виртуальные методы, которые перекрыть нельзя:

    TAbstractClass = class abstract

    public

    procedure Bar; virtual;

    end;

    TFinalMethodClass = class(TAbstractClass)

    public

    procedure Bar; override; final;

    end;

    Переопределить «Bar» уже больше нельзя.

    Статические методы класса

    У классов могут быть статические методы класса – то есть методы, которые можно вызвать от типа класса. Такие методы не имеют доступа к полям класса (также как и не могут получить Self на конкретный экземпляр данного класса):

    type

    TMyClass = class

    strict private

    class var

    FX: Integer;

    strict protected

    // Note: accessors for class properties must be declared class static.

    class function GetX: Integer; static;

    class procedure SetX(val: Integer); static;

    public

    class property X: Integer read GetX write SetX;

    class procedure StatProc(s: String); static;

    end;

    TMyClass.X := 17;

    TMyClass.StatProc(’Hello’);

    Здесь же представлен пример организации свойств классов. Их использование полностью аналогично использованию переменных и констант класса.

    for-element-in-collection

    Теперь компилятор способен распознавать итерации в контейнерах:

    for Element in ArrayExpr do Stmt;

    for Element in StringExpr do Stmt;

    for Element in SetExpr do Stmt;

    for Element in CollectionExpr do Stmt;

    Вот развернутый пример:

    var

    A: Array [1..6] of String;

    I: String;

    ….

    for i in A do

    begin

    Memo1.Lines.Add(i);

    end;

    Обратите внимание, что I имеет тип String это не индекс массива, а конкретные значения, которые будут получаться из массива. Кое-где конечно автоматизирует, но представьте, что мне нужно написать некий метод, в котором происходит копирование одного массива в другой. Использовать все равно придется стандартный цикл for, либо писать еще один метод – добавление элемента в массив.

    Динамическая инициализация массивов

    Теперь массивы получили свои конструкторы:

    Type

    TMas = Array of String;

    Var

    Mas: TMas;

    Mas := TMas.Create(‘Hello’, ’World’, ’!’);

    Я, конечно, не против «Create» как конструктора по умолчанию, но уже сейчас из-за отсутствия внятной русскоязычной литературы по данной теме встречаются статьи, в которых авторитетные господа пишут, что конструктор обязательно должен называться Create (речь идет не только о массивах, но также о записях и конструкторах класса). Так вот конструктор должен называться Create только для массивов. Для всех остальных имя конструктора не обязательно должно быть Create (но желательно, особенно для классов).

    Дженерики

    Шаблоны, они и в С++ шаблоны. Считается что первые шаблоны возникли в С++, но вообще-то они пришли из функционального программирования и правильное их название параметрический полиморфизм. Явление, когда компилятор сам вырабатывает соответствующие варианты кода на основании обобщенного алгоритма:

    TList<T> = class

    private

    FItems: array of T;

    FCount: Integer;

    procedure Grow(ACapacity: Integer);

    function GetItem(AIndex: Integer): T;

    procedure SetItem(AIndex: Integer; AValue: T);

    public

    procedure Add(const AItem: T);

    procedure AddRange(const AItems: array of T);

    procedure RemoveAt(AIndex: Integer);

    procedure Clear;

    property Item[AIndex: Integer]: T read GetItem write SetItem; default;

    property Count: Integer read FCount;

    end;

    Вот пример списка содержащего произвольные (но однотипные элементы). Тип элементов определяется на момент объявления переменной:

    ilist: TList<Integer>;

    То есть мы создали список целых чисел (а можно, к примеру, список строк). Дженерики удобно использовать применительно к алгоритмам контейнеров данных и комбинаторным алгоритмам. Конкретные реализации алгоритмов можно посмотреть в модуле Generics.Collections, где есть TArray, TList, TStack, TQueue, TDictionary, TObjectList, TObjectQueue, TObjectStack, TObjectDictionary и TEnumerator, способные работать с разными типами данных.

    Также необходимо отметить особенность дженериков (и шаблонов в С++) – обобщенные алгоритмы экономят время программиста, сокращая только его код, но для каждого типа (для каждой комбинации типов) всегда генерируется новая версия алгоритма (поэтому размер скомпилированных программ увеличивается).

    Заключение

    Большинство механизмов представленных здесь:

    . обеспечивают совместимость с .NET

    . дань моде

    . попытка угнаться за Microsoft Visual Studio

    Язык не содержит принципиальных отличий и мощных механизмов, которые действительно были бы востребованы именно программистами на языке Дельфи. Все нововведения навязаны, искусственны и не всегда соответствуют концепциям ООП. Большое количество противоречивых инструментов может только запутать программистов и в течение ближайших лет можно ожидать некоторого количества критических статей в адрес языка программирования Дельфи.

    Комментарий автора

    Личные впечатления о среде сложились следующие: сплошные недоделки (да и в 2009-м не лучше), ждать следующую версию наверно не стоит. FrameWork идет в комплекте, ничего доустанавливать не надо. Несмотря на заявленные требования не ниже 1 гигабайта ОЗУ, у меня и при 512-ти с тормозами, но работает.

    Ресурсы

    . Хроники «айтишника» http://skiminog.livejournal.com/33610.html

    . Общество разработчиков Embecadero http://edn.embarcadero.com

    . Углубленный материал по перегрузке операторов в Дельфи http://www.realcoding.net/articles/delphinet-peregruzka-operatorov.html.

    . Онлайн-перевод англоязычных материалов статьи http://www.translate.ru

    Похожие статьи



    Просмотров: 370 | Добавил: fortanted | Рейтинг: 0.0/0
    Всего комментариев: 0