ПЕРЕЛІК ДИСЦИПЛІН:
  • Адміністративне право
  • Арбітражний процес
  • Архітектура
  • Астрологія
  • Астрономія
  • Банківська справа
  • Безпека життєдіяльності
  • Біографії
  • Біологія
  • Біологія і хімія
  • Ботаніка та сільське гос-во
  • Бухгалтерський облік і аудит
  • Валютні відносини
  • Ветеринарія
  • Військова кафедра
  • Географія
  • Геодезія
  • Геологія
  • Етика
  • Держава і право
  • Цивільне право і процес
  • Діловодство
  • Гроші та кредит
  • Природничі науки
  • Журналістика
  • Екологія
  • Видавнича справа та поліграфія
  • Інвестиції
  • Іноземна мова
  • Інформатика
  • Інформатика, програмування
  • Юрист по наследству
  • Історичні особистості
  • Історія
  • Історія техніки
  • Кибернетика
  • Комунікації і зв'язок
  • Комп'ютерні науки
  • Косметологія
  • Короткий зміст творів
  • Криміналістика
  • Кримінологія
  • Криптология
  • Кулінарія
  • Культура і мистецтво
  • Культурологія
  • Російська література
  • Література і російська мова
  • Логіка
  • Логістика
  • Маркетинг
  • Математика
  • Медицина, здоров'я
  • Медичні науки
  • Міжнародне публічне право
  • Міжнародне приватне право
  • Міжнародні відносини
  • Менеджмент
  • Металургія
  • Москвоведение
  • Мовознавство
  • Музика
  • Муніципальне право
  • Податки, оподаткування
  •  
    Бесплатные рефераты
     

     

     

     

     

     

         
     
    Сім чудес і два фокусу на Дельфі
         

     

    Інформатика, програмування

    Сім чудес і два фокусу на Дельфі

    Максим Кузьмінський

    вірите Ви в дива чи ні, Ви, напевно, погодитеся зі мною, що іноді щось таке трапляється з кодом наших програм, і вони раптом перестають компілюватися або, що ще підступніше, починають видавати абсолютно непередбачуваний результат. І ось тоді, ви згодні, вас починають відвідувати дивні думки про участь у всі ці чудеса якихось потойбічних сил.

    В цій статті ми спробуємо сдернуть таємничий покрив з кількох, самих простих "чудес" і переконаємося, що все це - лише обман, ілюзія, а найчастіше - майстерне шахрайство.

    Ми розглянемо сім (з багатьох) таких чудес і спробуємо розгадати їх секрети. Зрозумівши механізм їх походження, ми, в ув'язненні, покажемо два приклади використання цих таємних сил у "мирних цілях". Наша мета - краще дізнатися Delphi і в майбутньому уникнути деяких що важко помилок.

    Для того, що б ви зрозуміли, що я маю на увазі, давайте розглянемо один дуже простий приклад.

    Чудо Перше (Round Miracle).

    Відкрийте Delphi, створіть новий проект, назвіть його AllMiracles, покладіть кнопку на головну форму і напишіть в обробнику події OnClick наступний код:

    procedure TfrmAllMiracles.btnRoundMrclClick (Sender: TObject);

    begin

    ShowMessage (IntToStr (Round (3.5) -- Round (2.5)));

    end;

    Figure 1.

    А тепер зупиніться і скажіть, який результат ви очікуєте побачити. Я сподіваюся ви не сказали "1", бо інакше це не було б диво. Ті, у кого добре розвинута інтуїція, можуть сказати "0", і це буде ще далі від правильної відповіді. І лише ті, хто часто грає в Спортлото або, у найгіршому кінець, уважно читає документацію, відповість "2" і це буде правильно. Не вірите? - Тисніть F9.

    Читаємо Help по функції Round:

    Round returns an Int64 value that is the value of X rounded to the nearest whole number. If X is exactly halfway between two whole numbers, the result is always the even number.

    Ось таке воно, "Кругле диво".

    Сподіваюся, тепер ви зрозуміли, про що ми будемо говорити сьогодні. У цій статті немає важких, хитромудрих прикладів. Код - гранично спрощено що б виділити саму суть проблеми. А наше з вами справа - розібратися в ній і, якщо можна, виправити ситуацію. Як, наприклад, в наступному випадку.

    Чудо Друге (Absolute Miracle).

    Покладіть на головну форму створеного раніше проекту нову кнопку і напишіть в його обробнику події OnClick такий код:

    procedure TfrmAllMiracles.btnAbsMrclClick (Sender: TObject);

    var

    i1: int64;

    begin

    i1: = abs (low (integer ));

    ShowMessage (IntToStr (i1 ));

    end;

    Figure 2.

    Перш ніж натиснути F9, проаналізуємо написане. Low від integer - значення відоме всім, записане навіть у Help'е і рівне -2147483648, тобто число негативне.

    Help не говорить про функції Abs нічого нового:

    Abs returns the absolute value of the argument X. X is an integer-type or real-type expression.

    Змінна i1 описана як int64, і це правильно, тому що 2147483648 - вже виходить за кордону типу integer. Це значення (2147483648) ми і очікуємо побачити на екрані, чи не так? А ось і ні. Перевірте. На екрані знову - 2147483648. Як абсолютна може мати негативне?

    Давайте ще раз, уважніше розглянемо вираз abs (low (integer)). Що можна ще сказати про нього? Не дивлячись на наявність в ньому функцій, це - константа

    Читаємо Help за темою "Constant expressions":

    ... Constant expressions cannot include variables, pointers, or function calls, except calls to the following predefined functions: Abs ... Low ...

    спробуємо описати константу із значенням рівним цього виразу:

    ...

    const

    ci = abs (low (integer ));

    ...

    Figure 3.

    Код компілюється. Значить ми - праві, а це означає, що результат вираження визначається ще на стадії компіляції. Далі, low (integer)) має цілий тип. Abs від integer - теж ціле, а нам потрібно int64. Поробуем переписати код наступним чином:

    procedure TfrmAllMiracles.btnAbsMrclClick (Sender: TObject);

    const

    ci = abs (low (integer ));

    var

    i1: int64;

    begin

    // i1: = abs ((low (integer )));

    i1: = abs (int64 (low (integer )));

    ShowMessage (IntToStr (i1 ));

    end;

    Figure 4.

    Тепер - Запрацювало. Секрет "Абсолютного дива" розкритий! До речі, abs (int64 (low (integer))) - теж константа.

    Наступний диво - приклад того, як цілком правильний код відмовляється компілюватися.

    Чудо третє (One more low integer miracle).

    Нова кнопка на формі буде реагувати на натискання наступним чином:

    procedure TfrmAllMiracles.btnLowIntMrclClick (Sender: TObject);

    var

    lowInt: integer;

    begin

    lowInt: = -2147483648;

    ShowMessageFmt ( '% d', [lowInt ]);

    end;

    Figure 4.

    Цілком звичайна процедура. У нас виникло бажання привласнити деякої змінної цілком законне значення. Але цей код НЕ компілюється:

    Overflow in conversion or arithmetic operation

    Тиснемо F1 на повідомленні про помилку і читаємо:

    The compiler has detected an overflow in an arithmetic expression: the result of the expression is too large to be represented in 32 bits.

    Мабуть компілятор намагається визначити константи цілого типу зі значенням 2147483648, а тільки потім змінити її знак, але це йому не вдається. Перепишемо код:

    procedure TfrmAllMiracles.btnLowIntMrclClick (Sender: TObject);

    var

    lowInt: integer;

    begin

    lowInt: =-int64 (2147483648);

    // lowInt: = -2147483648;

    ShowMessageFmt ( '% d', [lowInt ]);

    end;

    Figure 5.

    Ось тепер - все нормально. Приклад дуже простий, але дає нам уявлення про те, як компілятор Delphi обробляє константи і визначає їх тип.

    А ось таке диво - приклад того, до якої плутанини може призвести перевантаження функцій. Такі чудеса ми найчастіше самі влаштовуємо собі через неуважність, а потім годинами шукаємо помилки.

    Чудо четверте (String Trick).

    Ну, що ж, додамо знову кнопку на нашу форму і задамо наступний код для події OnClick:

    procedure TfrmAllMiracles.btnCopyMrclClick (Sender: TObject);

    const

    cs: array [0 .. 1] of char = '01 ';

    begin

    ShowMessage (copy (cs, 0,1) + copy (cs, 1,1 ));

    end;

    Figure 6.

    Я знаю, що ви вже чекаєте каверзи і все ж таки результат може виявитися несподіваним: "00".

    Як звичайно звернемося до help'у, дивимося функцію Copy:

    Returns a substring of a string or a segment of a dynamic array.

    ...

    function Copy (S; Index, Count: Integer): string;

    function Copy (S; Index, Count: Integer): array;

    ...

    Справа в тому, що у виразі copy (cs, 0,1) + copy (cs, 1,1) обидва рази викликаються різні версії функції copy, перший раз - для динамічних масивів, які нумеруються з 0, а другий раз - для рядків, перший елемент яких має індекс 1. Обидва рази cs перетвориться до необхідного типу, і те, що cs, як масив починається з нульового елементу, в даному випадку не має ніякого значення.

    А тепер, нарешті, ми добралися і до об'єктів. Безліч Дельфійських чудес пов'язані з тим, що об'єкти в Delphi - автоматично разименуемие посилання, які можуть вказувати на звільнену або зайняту кимось іншим область пам'яті. Про такі випадки написано чимало. Наше чудо - інше.

    Чудо п'ятий (Is-Miracle).

    Опишіть у розділі protected нашої форми поле FControl типу TСontrol і задайте для ще одній - нової кнопки ось таку реакцію на її натискання:

    procedure TfrmAllMiracles.btnIsMrclClick (Sender: TObject);

    begin

    if (FControl is TControl) then

    begin

    if not Assigned (FControl) then

    FControl: = TControl.Create (Self);

    end

    else

    ShowMessage ( 'Not a Control');

    end;

    Figure 7.

    Таке "Чудо" я бачив кілька разів і в різних проявах. Скільки раз б ви не натискали на кнопку btnIsMrcl, ви кожного разу будете бачити повідомлення 'Not a Control ', а конструктор TControl так ніколи і не буде викликаний.

    Ось, що говорить Help:

    ... The expression object is class returns True if object is an instance of the class denoted by class or one of its descendants, and False otherwise. (If object is nil, the result is False.)

    Справа в тому, що оператор is використовує посилання на клас об'єкта, а не те, як описана змінна, яка по суті - простий покажчик. Так що TControl не завжди TControl.

    Так, я сподіваюся ви розумієте, що TControl тут обрано випадково, з таким же успіхом це міг бути і будь-який інший клас.

    Випадок коли FControl посилається на вже звільнений об'єкт або є локальної та непроініціалізірованной змінної, дає непредказуемие результати і може призвести до зовсім не чудовому краху аплікації.

    А от для наступного чуда я знайшов тільки непрямий обьяснение в Help'е і тому ми будемо змушені провести невеликий експеримент.

    Чудо шосте (Is-Miracle II)

    Давайте подивимося ще на одне, схоже диво пов'язане з оператором is. Додамо до нашої групі проектів (ProjectGroup1) новий проект - DLL з ім'ям AllMirrLib, в єдиному модулі якого буде наступний код:

    library AllMirrLib;

    uses

    Controls;

    function IsControlLib (const anObj: TObject): boolean;

    begin

    Result: = anObj is TControl;

    end;

    exports

    IsControlLib;

    Figure 9.

    Як ви бачите ця бібліотека експортує тільки одну дуже просту функцію, яка повертає знеченіе True в тому випадку, якщо її єдиний параметр походить від TControl і False - в інших випадках.

    В модуль форми нашого основного проекту додамо наступне визначення:

    unit AllMir;

    interface

    ...

    implementation

    ($ R *. DFM)

    function IsControlLib (const anObj: TObject): boolean; external 'AllMirrLib.DLL';

    Figure 10.

    Тепер, як завжди, додамо на форму нову кнопку:

    procedure TfrmAllMiracles.btnIsMrcl2Click (Sender: TObject);

    begin

    FControl: = TControl.Create (nil);

    try

    if not IsControlLib (FControl) then

    ShowMessage ( 'Not a Control');

    finally

    FreeAndNil (FControl);

    end;

    end;

    Figure 11.

    Як ви вже напевно здогадалися FControl знову виявиться не TControl. Знайдіть у модулі System процедуру _IsClass. Хоч вона й написана на асемблері, неважко зрозуміти, що в ній відбувається - в циклі проглядаються посилання на класи (спочатку власна - об'єкта, а потім - усіх предків) і серед них шукається рівна правий операнд. Давайте трохи змінимо процедуру:

    procedure TfrmAllMiracles.btnIsMrcl2Click (Sender: TObject);

    var

    p1, p2: pointer;

    begin

    FControl: = TControl.Create (nil);

    try

    p1: = pointer (FControl.ClassType);

    p2: = pointer (TControl);

    if not IsControlLib (FControl) then

    ShowMessage ( 'Not a Control');

    finally

    FreeAndNil (FControl);

    end;

    end;

    Figure 12.

    Подивіться під відладчиком значення p1 і p2 - вони рівні. Тепер змінимо і функцію IsControlLib: TObject): boolean;

    var

    p3, p4: pointer;

    begin

    p3: = pointer (anObj.ClassType);

    p4: = pointer (TControl);

    Result: = anObj is TControl;

    end;

    Figure 13.

    Тут теж поставимо крапку зупинення і порівняємо значення. Змінні p1, p2 та p3 мають одне і теж значення, а от p4 - вказує куди-то не туди. Проблема в тому, що в аплікації і в DLL співіснують два різні класи TControl, ось тому равества бути й не може.

    Непряме вказівка на цю проблему в Help'е можна знайти в описі методу ClassNameIs.

    Читаємо Help:

    Use ClassNameIs when writing conditional code based on an object's type or to query objects across modules, or DLLs.

    Так, до речі, не забудьте, що у вас два проекти в групі і компілюється завжди тільки активний проект. Так що не забувайте перпеключаться на потрібний проект з міру необхідності або компілюється відразу все: Alt-P, U.

    Наступний чудо я зустрів у програмі одного починаючого програміста і воно було звичайно злегка закамуфльований, так що я, на свій сором, навіть не відразу зрозумів, у чому справа. Я бачив значення змінних, знав, що це - змінні типу variant, але ніяк не міг зрозуміти чому результат обчислення якогось нескладного вираження весь час помилковий. Перевірте себе і ви.

    Чудо сьоме (Miracle with Variants).

    Як ви вже здогадалися, почнемо з нової кнопки, яка виконує наступні дії при натисканні:

    procedure TfrmAllMiracles.btnVarMrclClick (Sender: TObject);

    var

    X, Y, Z: variant;

    begin

    X: = '1 ';

    Y: = '2 ';

    Z: = 3;

    ShowMessage (X + Y + Z);

    end;

    Figure 14.

    Можете Чи ви передбачити результат виразу '1 '+ '2' 3? Якщо ви сказали '6 ', то ви теж попалися. Подивимося уважніше, '1 '+ '2' буде ... звичайно '12 ', 12 +3 = 15. Це і є правильна відповідь.

    Отже, ми побачили сім чудес Delphi, сім - з багатьох. Це не означає, що вони -- яскраві або самі чудові. Але на них можна багато чому навчитися. Візьмемо останнє, щойно розглянуте нами, чудо. Задумайтесь, як Delphi вдається зводити в одному вираженні значення різних типів? А якщо один з членів виразу -- variant?

    Фокус перший (Variant trick)

    Читаємо Help в розділі "Variants in expressions":

    ... In a binary operation, if only one operand is a variant, the other is converted to a variant ..

    Не здається вам це дивним - variant можна складати з чим завгодно. Наприклад, integer плюс variant - буде variant, а variant можна знову складати з чим завгодно ...

    Нова кнопка на формі буде виконувати наступні дії:

    procedure TfrmAllMiracles.btnVarTrickClick (Sender: TObject);

    var

    v: variant;

    b: boolean;

    i: integer;

    s: string;

    d: TDatetime;

    x: Double;

    begin

    v: = 0;

    b: = true;

    i: = 2;

    s: = '3 ';

    d: = StrToDateTime ('01/01/01');

    x: = 5;

    v: = v + b + i + s + d + x;

    ShowMessage (VarToStr (v ));

    end;

    Figure 15.

    Не здається вам, що чудо вже те, що цей код компілюється, але ж він ще й видає якийсь результат. Адже все дуже просто - "variant можна складати з чим завгодно "і знову отримаємо - variant.

    Одного разу до мене звернувся один мій знайомий з питанням чи немає в Delphi чогось подібного прихованого параметру Self, але для оператора with. Ні - відповів я йому спершу, а потім задумався ...

    Фокус другий (With-trick)

    Припустимо у нас є наступна функція:

    procedure ShowText (sl: TStringList);

    begin

    ShowMessage (sl.text);

    end;

    Figure 16.

    І кнопка на формі:

    procedure TfrmAllMiracles.btnWithSelfTrickClick (Sender: TObject);

    var

    sl: TStringList;

    begin

    sl: = TStringList.Create;

    try

    sl.CommaText: = '1, 2,3,4,5,6,7,8,9,0 ';

    ShowText (sl);

    finally

    sl.Free;

    end;

    end;

    Figure 17.

    І ми, з якихось причин, хочемо позбавитися від локальної змінної sl. Але для того, що б звернутися до функції ShowText, ми повинні передати їй параметр типу TStringList. Звідки ж його взяти?

    Давайте поміркуємо. Кожен метод отримує прихований параметр Self, може бути як-то можна витягнути його звідти? Писати для цього спеціальний метод якогось класу не хотілося б - адже це працювало б тільки для його нащадків.

    Давайте почитаємо Help, розділ "TMethod type":

    ... This type can be used in a type cast of a method pointer to access the code and data parts of the method pointer ...

    Не чи це те, що ми шукаємо?

    Визначимо тип і функцію:

    type

    TSimpleMethod = procedure of object;

    function GetWithSelf (const pr: TSimpleMethod): TObject;

    begin

    Result: = TMethod (pr). Data;

    end;

    Figure 18.

    Як бачите, функція приймає покажчик на метод, а повертає об'єкт, що є власником цього методу. Але яким же методом ми скористаємося? Наприклад, метод Free, адже його історія сягає ще до самого TObject'у. Тепер перевіримо себе:

    procedure TfrmAllMiracles.btnWithSelfTrickClick (Sender: TObject);

    begin

    with TStringList.Create do

    try

    CommaText: = '1, 2,3,4,5,6,7,8,9,0 ';

    ShowText (TStringList (GetWithSelf (Free )));

    finally

    Free;

    end;

    end;

    Figure 19.

    перевірки - Працює.

         
     
         
    Реферат Банк
     
    Рефераты
     
    Бесплатные рефераты
     

     

     

     

     

     

     

     
     
     
      Все права защищены. Reff.net.ua - українські реферати ! DMCA.com Protection Status