Про використання в Delphi класів

1. Вимоги


Передбачається: знання Delphi на рівні використання DLL, а також написання власних; знання С + + на рівні написання найпростішого додатка в середовищі MS VC + +.


Бажано: загальне розуміння угод про виклик функцій; загальне уявлення про способи передачі параметрів і повернення значення.
Використовувані інструменти: Borland Delphi 6, MS VC + + 6.0


2. Обгрунтування


Необхідність використання чужого коду у своїй програмі виникає регулярно. Вставка готових вдалих рішень дозволяє не винаходити велосипед заново. У хороших випадках чужий код написаний на тому ж мовою, що і свій, або рішення оформлене у вигляді DLL або компонента. Однак, бувають випадки гірше. Наприклад, купується PCI-плата розширення з програмним забезпеченням для неї, а це ПЗ виявляється файлами вихідного коду на С або С + +, у той час як проект вже розпочато на Delphi, і, крім того, в команді розробників С + + знають погано.


3. Варіанти рішення


У принципі, можна весь проект писати на С + +. Якщо така можливість є – не виключено, що це кращий вихід. Але користувальницький інтерфейс в Delphi розробляється швидше, ніж у MS VC + + (не тільки моя думка, але гарну цитату не знайшов), крім того, у групі можуть погано знати С + +. І якщо навіть С + + знають добре, але проект вже розпочато на Delphi, переписувати готове – значить, витрачати неоплачуваний час.


Можна переписати код С + + на Delphi. Для цього потрібен час, і, можливо, чимале, а головне – знання С + + на рівні істотно вище початкового ("читаю зі словником"). При цьому, багато мовні конструкції С + + не мають прямих аналогів в Delphi, і їх перенесення загрожує появою помилок, в тому числі, абсолютно безглуздих, і тому трудноотлавліваемих. Зокрема, прекрасний приклад з обговорення статті "ЯП, ОПП і т.д. і т.п. у світлі безпеки програмування ":


for(;P(”
“),R-;P(“/”)) for(e=C;e-;P(“_”+(*u++/8)%2))P(“/ “+(*u/4)%2);

Можна спробувати засікти час і перекласти це на Pascal. Чи стане приблизно зрозуміло, скільки часу піде на переклад класу, де подібні конструкції не поодинокі.


Можна скористатися інтерфейсами і технологією СОМ (мабуть, точніше – моделлю СОМ і технологією ActiveX). Але – ось цитата з [1], глава "Модель багатокомпонентних об'єктів":
"І ще одне зауваження: не думайте, що шлях буде легким. Крейг Брокшмідт говорив, що перед тим, як він почав розуміти ці концепції, у нього був" шість місяців туман в голові. "Мінімальна передумова – Вичерпне знання мови С + +. "Кінець цитати. І, хоча" модель СОМ надає уніфікований, відкритий, об'єктно-орієнтований протокол зв'язку між програмами "(цитата звідти ж), вона вимагає такої кваліфікації від програміста, яка рідко зустрічається в середовищі непрофесіоналів.


Можна реалізувати код, який необхідно використовувати, у вигляді DLL. Один з істотних плюсів DLL – неважливо, якою мовою вона написана, якщо дотримані деякі умови, такі, як угоду про виклик і сумісність типів.


З урахуванням того, що в групі розробників в основному про С + + поверхові уявлення, а СОМ – незнайома абревіатура, і, при цьому, термін здачі проекту – традиційно – вчора, нічого кращого варіанту з DLL у нас придумати не вийшло.


4. Трохи теорії


Передати, точніше, експортувати більше функцій, DLL – не проблема. Наводимо типи, угоди про виклики, заповнюємо список експортованих функцій – і все (в основному). Про це написано чимало, наприклад, в [2], у підпункті "Використання DLL, розроблених в С + +".


Експортувати клас дещо складніше. Навіть якщо і DLL, і основна програма написані на Delphi, виникають проблеми з розподілом пам'яті, які вирішуються використанням модуля ShаreMem, першим у списку uses як проекту, так і DLL [2, 3]. Причому, цей модуль можна, в принципі, замінити саморобним менеджером пам'яті [там же, 3]. Але як використовувати ShаreMem, якщо DLL написана на іншій мові, або написати власний менеджер для двох мов? Напевно, можна і так, але, нагадую, термін здачі проекту – вчора. Якщо є й інші заперечення, часто час – визначальний фактор.


Можна створювати екземпляр класу при завантаженні DLL, ліквідувати при вивантаженні (використовуючи події DLL_PROCESS_ATTACH / DETACH), а для методів класу (функцій-членів, раз вже клас на С + +) написати однорядкові функції-обгортки, які не є членами, а просто викликають відповідні функції-члени. Негарно, і багато зайвої роботи. Спробуємо все ж експортувати клас.


В [2], сказано: "Бібліотеки DLL не можуть експортувати класи та об'єкти, якщо тільки ви не використовуєте спеціалізовану технологію Microsoft під назвою СОМ або яку-небудь іншу вдосконалену технологію ". Втім, там же є зауваження:" Насправді об'єкти можуть бути передані з DLL в зухвалу програму у разі, якщо ці об'єкти спроектовані для використання як інтерфейсів або чистих абстрактних класів ". Крім цього зауваження, в [2] про експорт об'єктів майже всі, але вже добре, що є шанс" зробити це по-швидкому ".


І, нарешті, в [4] знаходимо параграф "Експорт об'єктів з DLL". Там сказано: "До об'єкту і його методам можна отримати доступ, навіть якщо цей об'єкт міститься всередині DLL. Але до визначення такого об'єкта всередині DLL і його використання пред'являються певні вимоги. Иллюстрируемого тут підхід застосовується у вельми специфічних ситуаціях, і, як правило, такого ж ефекту можна досягти шляхом застосування пакетів або інтерфейсів ". Наша ситуація цілком специфічна; пакети тут неприйнятні, оскільки вони все-таки для використання з Delphi, про використання інтерфейсів і СОМ вже сказано, а використовувати інтерфейси без СОМ поза Delphi, судячи з [2], не можна.


І, мабуть, найважливіше з [4]:
"На експорт об'єктів з DLL накладаються наступні обмеження:


  1. Зухвала додаток може використовувати лише ті методи об'єкта, які були оголошені як віртуальні.
  2. Примірники об'єктів повинні створюватися усередині DLL.
  3. Експортований об'єкт повинен бути визначений як в DLL, так і в зухвалій додатку за допомогою методів, визначених у тому ж порядку.
  4. З об'єкта, що міститься вутрі DLL, не можна створити об'єкт-нащадок.

Тут перераховані лише основні обмеження, але можливі і деякі інші. "

Далі там розповідається про роботу з DLL, написаної в Delphi, але отриманої інформації достатньо для роботи з DLL, створюваної в MS VC + +.


Майстер MS VC + + дозволяє створити звичайну (regular) DLL і DLL-розширення (extension). Звичайна DLL може експортувати тільки С-функції і не здатна експортувати С + +-класи, функції-члени або перевизначені функції [1]. Стало бути, треба використовувати DLL-розширення. Майстер створить заготівлю, потім в каталог проекту треба буде скопіювати два файли – заголовний і файл коду (*. h та *. cpp), що містять клас, з примірником якого належить попрацювати. Потім підключити їх до проекту DLL і трохи допрацювати відповідно до зазначених обмеженнями.


По-перше, всі експортовані відкриті методи (ну, або функції-члени, як хочете) необхідно оголосити з директивою __stdcall, зі зрозумілих причин. По-друге, їх також необхідно оголосити віртуальними. Це робиться для того, щоб точки входу виявилися записаними в таблицю віртуальних функцій (VMT), через яку і буде здійснюватись експорт-імпорт. По-третє, клас потрібно оголосити з макроозначень директивою AFX_EXT_CLASS або AFX_CLASS_EXPORT, це синоніми. Зроблені зміни не впливають на працездатність класу в ехе-проекті, навіть директива експортованого класу.


Далі у файл. Срр проекту DLL потрібно додати функції створення і ліквідації об'єкта. Приклад в [4] обходиться без функції ліквідації, мабуть, тому, що в наведеному там прикладі і DLL і імпортує додаток написані на Delphi, так що можна звільнити пам'ять методом Free, який є у всіх спадкоємців TObject і відсутній у об'єктів С + +, що не мають загального класу-предка. Функція створення об'єкта повинна просто викликати конструктор, передати йому отримані від програми параметри і повернути покажчик на створений об'єкт. Функція ліквідації приймає покажчик на об'єкт і викликає деструктор. І обязятельно вписати ці функції в список експортованих.


І все! П'ятнадцять хвилин роботи, при мінімальному знанні С + +. Останнє в Delphi.


У імпортує програмі необхідно оголосити клас, що містить віртуальні відкриті функції в тому ж порядку. Також необхідно оголосити складні структури даних, що використовуються в DLL і передані через її кордон у будь-якому напрямку. Маються на увазі структури С + +, вони ж записи Паскаля. І, звичайно ж, потрібно оголосити імпортовані функції створення і знищення класу. Тепер для створення екземпляра класу викликається відповідна функція DLL, коли об'єкт перестає бути потрібним – знову викликається відповідна функція DLL, а методи викликаються традиційно – "Об'ект.Метод (Параметри)". При цьому обзивати методи в Delphi можна як завгодно, важливий лише порядок їхнього проходження і списки параметрів.


Якщо в С + + функція-член повертає значення, в Delphi відповідний метод повинен бути теж функцією. Якщо ж функція-член повертає void, в Delphi відповідний метод – процедура.


Якщо в С + + параметр передається за значенням, то і в Delphi теж, якщо ж по посиланню (тобто як індикатор), то в Delphi такий параметр повинен бути оголошений з ключовим словом var.


Трохи докладніше про параметри та їх типах. Практично скрізь, де йдеться про DLL, згадується, що, якщо хочете забезпечити сумісність DLL з програмами на інших мовах, необхідно забезпечити сумісність типів. Тобто, прагнути використовувати стандартні типи ОС Windоws. Такі типи, як string або file взагалі не сумісні з С + +, з TDateTime можна поекспериментувати, взагалі-то, він відповідає стандарту, прийнятому в OLE-автоматизації ([3]). Знову ж таки, [3] заявляє про відповідність типів single і double Delphi з float і double в С + + відповідно. Хоча в [5] є такий рада з посиланням на News Group: "Якщо ви створюєте DLL не за допомогою Delphi, то уникайте чисел з плаваючою крапкою в повернутому значенні. Замість цього використовуйте var-параметр (індикатор або контрольний параметр в С + +) Причина криється в тому, що Borland та Microsoft застосовують різні способи повернення чисел з плаваючою крапкою. Borland С + + і Delphi можуть використовувати один і той же метод ".

























































Таблиця відповідності типів Delphi і С + +:
Тип С + + Байтів Тип Delphi
int ?(4) integer
unsigned int ?(4) cardinal
char, __int8 1 shortint
short, __int16 2 smallint
long, __int32 (int) 4 longint (integer)
__int64 8 int64
unsigned char 1 byte
unsigned short 2 word
unsigned long 4 longword
float 4 single
double 8 double
char *   PChar

5. Трохи практики


Для прикладу буде використаний нескладний і даремний клас на С + +, зліплений на ходу. У MS VC + + створимо проект, використовуючи MFC AppWizard (exe), без використання представлення "Документ-вид", на основі діалогу, і обізву його "example_exe". Додамо два нових файлу – example.cpp і example.h.


Файл example.h:

 //************************************************ *****************************
/ / Традиційний фінт вухами щоб уникнути
/ / Повторного включення файлу. H
#if !defined(EXAMPLE__INCLUDED)
#define EXAMPLE__INCLUDED
/ / Введемо парочку структур для демонстрації роботи з ними
typedef struct
{
int n;
int i;
short j;
char k;
}struct_1;

typedef struct
{
int n2;
short a[3];
}struct_2;

/ / Клас-приклад. Нічого корисного, просто демонстрація.
class CExample
{
private:
int Field;
CString Name;
void Show(CString str);
public:
/ / Конструктор і деструктор, як належить
CExample(int F, CString N);
~CExample();
/ / Просто повідомлення
void Message(CString str, int Digit);

/ / "Процедура" і "функція"
void Proc(int * Digit);
int Func(int Number);

/ / Робота з закритим полем
void SetF(int F);
int GetF();

/ / Робота зі структурами
struct_2 * Struct1to2(struct_1 s1);
};
#endif //if !defined(EXAMPLE__INCLUDED)
//************************************************ *****************************


У класі є пара закритих полів, закрита функція-член, набір відкритих функцій. Конструктор приймає два параметри. Рядковий параметр будемо інтерпретувати, як ім'я об'єкта. Функція Message потрібна для відображення на екрані хоч якихось повідомлень, демонструють, що щось відбувається. Proc імітує процедуру, тобто, не повертає значення, зате змінює щось у програмі, в нашому випадку, переданий параметр. Func і є функція, тобто, нічого не змінює, зате обчислює деяке значення і повертає його. Плюс тут же установник і зчитувач закритого поля, а також простенька демонстрація роботи зі структурами.


Файл example.срр:

 //************************************************ *****************************
#include “stdafx.h”
#include “Example.h”

/ / Конструктор ініціалізує два закритих поля
/ / І видає повідомлення про успішне створення
/ / За допомогою закритої функції
CExample::CExample(int F, CString N)
{
this->Field = F;
this->Name = N;
this->Show(N + ” Created successfully”);
}

/ / Деструктор тільки сообщcellpadding ает про самоліквідацію
CExample::~CExample()
{
this->Show(“Deleted successfully”);
}

/ / Закрита функція, по суті – оболонка MessageBox "а
/ / Заголовком боксу буде ім'я класу
void CExample::Show(CString str)
{
::MessageBox(NULL, str, this->Name, MB_OK);
}

/ / Відкрита функція, виводить рядок і число в десятковому вигляді.
void CExample::Message(CString str, int Digit)
{
str.Format(str + ” %d”, Digit);
this->Show(str);
}

/ / "Процедура" не повертає значення, зате змінює параметр
void CExample::Proc(int * Digit)
{
*Digit *= 2;
}

/ / "Функція" не змінює параметр, зате повертає значення
int CExample::Func(int Number)
{
int Result;
Result = Number * 2;
return Result;
}

/ / Банально присвоюємо значення параметра закритому полю.
void CExample::SetF(int F)
{
this->Field = F;
}

/ / Ще банальніше …
int CExample::GetF()
{
return this->Field;
}

/ / Присвоюємо значення полів однієї структури полях інший
struct_2 * CExample::Struct1to2(struct_1 s1)
{
struct_2 * s2 = new struct_2;

s2->n2 = s1.n * 2;
s2->a[0] = s1.i;
s2->a[1] = s1.j;
s2->a[2] = s1.k;
return s2;
}
//************************************************ *****************************


Для прикладу більш, ніж достатньо. Тепер треба подивитися, як це працює.

У файлі Example_exeDlg.h в описі класу CExample_exeDlg де-небудь в секції public треба вписати
CExample * ex;
тобто, оголосити змінну-член, покажчик на наш навчально-тренувальний клас, і в конструкторі Example_exeDlg вписати
ex = NULL;
Можна ex зробити і не членом, в принципі, й ініціювати при оголошенні. І, звичайно, не забути нагорі цього ж файлу вклеїти заголовний файл класу:
#include “Example.h”

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

void CExample_exeDlg::OnBtCreate()
{
if (ex == NULL)
ex = new CExample(7, “Example”);
}
Якщо об'єкт ще не створено – створюємо і ініціалізували пару закритих полів.

void CExample_exeDlg::OnBtDestroy()
{
delete ex;
ex = NULL;
}
Звільняємо пам'ять і встановлюємо покажчик в "порожньо"

void CExample_exeDlg::OnBtMessage()
{
ex->Message(“Any digit – “, 3);
}
Демонстраційне повідомлення.

void CExample_exeDlg::OnBtProc()
{
int k = 5;
ex->Message(“before K = “, k);
ex->Proc(&k);
ex->Message(“after K = “, k);
}
Показуємо в послідовних повідомленнях, яке значення мінлива мала до виконання процедури, і яке стала мати після.

void CExample_exeDlg::OnBtFunc()
{
int k = 5, l;
ex->Message(“before K = “, k);
l = ex->Func(k);
ex->Message(“after K = “, k);
ex->Message(“Result of Func = “, l);
}
Приблизно те ж саме – значення до виконання, значення після виконання і результат виконання.

void CExample_exeDlg::OnBtGet()
{
ex->Message(“”, ex->GetF());
}

void CExample_exeDlg::OnBtSet()
{
ex->SetF(ex->GetF() + 1);
}

Ці дві – без коментарів. Має бути так все зрозуміло … Функцію для роботи зі структурами в цьому проекті не буду чіпати, не цікаво, тут весь фокус, як їх передати через кордон DLL. Крім того, не будемо возитися з полями введення, а передамо параметри безпосередньо в коді. Наочність це зменшує ненабагато, а роботи менше. Ще момент – ID кнопок за замовчуванням поміняв з BUTTON1 на BT_CREATE і так далі, для наочності.


Все! На формі тільки кнопки, виведення інформації через MessageBox. Можна перевірити роботу.


Зробимо DLL для цього класу. У MS VC + + створимо проект, використовуючи MFC AppWizard (dll), назвемо "example_dll". У каталог цього проекту копіюємо готові example.cpp і example.h, додамо їх до проекту. Будемо змінювати, відповідно до з'ясованими правилами. Почнемо з оголошення класу:


// Можно использовать AFX_EXT_CLASS, это синонимы.
class AFX_CLASS_EXPORT CExample

Потім з
void Message(CString str, int Digit);
робимо
virtual void __stdcall Message(CString str, int Digit);
і так з усіма відкритими методами, крім конструктора і деструкції. І на цьому б усе, так CString – несумісний, небезпечний тип. Міняємо оголошення:
virtual void __stdcall Message (char * str, int Digit);
і визначення:
void CExample::Message (char* str, int Digit)
{
/ / Додаємо CString:
CString s = str;
/ / І трохи змінюємо роботу з рядком:
//str.Format(str + ” %d”, Digit);
s.Format(s + ” %d”, Digit);
//this->Show(str);
this->Show(s);
}
тобто, приходимо до сумісного типу "покажчик на нуль-термінальну рядок", але, щоб не втратити функціональність класу CString, оголошуємо локальну змінну цього класу і використовуємо її. Залишилося ще півтори деталі.
Перша деталь – у файлі example_dll.cpp в кінці пишемо:

// вставляем функцию инициализации..
CExample * __stdcall InitExample(int F, char * N)
{
CExample * ex;
/ / Транслюємо конструктору прийняті параметри
ex = new CExample(F, N);
/ / І повертаємо покажчик на створений об'єкт
return ex;
}

/ / .. І ліквідації
void __stdcall DelExample(CExample * ex)
{
delete ex;
}

І половина деталі – у файлі EXAMPLE_DLL.def в кінці дописуємо пару рядків, так, щоб вийшло:

;*****************************************************************************
; EXAMPLE_DLL.def : Declares the module parameters for the DLL.

LIBRARY “EXAMPLE_DLL”
DESCRIPTION “EXAMPLE_DLL Windows Dynamic Link Library”

EXPORTS
; Explicit exports can go here
InitExample
DelExample
;************************************************* ****************************

Після компіляції DLL готова. Підготуємо проект в Delphi, щоб продемонструвати її роботу. Створимо проект "Example_Delphi", і в модулі головної форми, перед оголошенням класу форми, впишемо чотири типи. Два – структури struct1 і 2:

TRec1 = record
n : integer;
i : integer;
j : smallint;
k : shortint;
end;
TRec2 = record
n2 : integer;
a : array[0..2] of smallint;
end;

Третій – покажчик на другу структуру:

PRec2 = ^TRec2;

А четвертий – наш клас, з яким будемо працювати:

TExample = class
public
procedure Mess_(str : PChar; Digit : integer); virtual; stdcall; abstract;
procedure Proc(var Digit : integer); virtual; stdcall; abstract;
function Func(Number : integer): integer; virtual; stdcall; abstract;
procedure SetF(F : integer); virtual; stdcall; abstract;
function GetF(): integer; virtual; stdcall; abstract;
function Struct1to2(rec1 : TRec1): PRec2; virtual; stdcall; abstract;
end;

Директиви virtual і stdcall коментарів не потребують. Про них сказано вище. А навіщо abstract? Дуже просто. Без неї компілятор буде лаятися на неправильне попереджуюче оголошення функції, адже опису її у нас немає, опис – у DLL. Директиви повинні йти саме в цьому порядку. Інакше компілятору не подобається.


Зверніть увагу на перший метод. Решта названі так само, як і в С + +, але слово Message в Delphi зарезервоване, і використовувати його не за призначенням не варто. Добре, назвемо інакше, важливо, що вона стоїть на першому місці серед віртуальних функцій, як і в С + +, значить, її знайдуть за номером в VMT. Ім'я ролі не грає.


Ще треба додати оголошення експортованих функцій створення / ліквідації, в кінці секції interface:

function InitExample(F: integer; N : PChar) : TExample; stdcall;
external “..Example_DLLdebugExample_DLL.dll”;
procedure DelExample(ex : TExample); stdcall;
external “..Example_DLLdebugExample_DLL.dll”;

Тут передбачається, що DLL лежить там, де і з'явилася після компіляції, а директорії "Example_dll" і "Example_Delphi" мають спільну батьківську. Більше ніде її шукати не будуть. Якщо ж вказати тільки ім'я, додаток буде шукати бібліотеку у своїй папці, папках WINDOWS, SYSTEM32 та прописані у змінній оточення PATH. Втім, це абетка.


Все, клас можна використовувати. Давайте знову наробимо кнопок, а висновок в полі Memo, благо, в Delphi з ним працювати швидше і простіше, ніж в MS VС + +.


Ось обробники кнопок:

procedure TForm1.Button1Click(Sender: TObject);
begin
if not Assigned(Self.ex) then
Self.ex := InitExample(10, “Ex_Delphi”);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
DelExample(Self.ex);
Self.ex := nil;
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
Self.ex.Mess_ (PChar ("Деяка цифра -"), 5);
end;

procedure TForm1.Button4Click(Sender: TObject);
var
j : integer;
begin
j := 15;
Self.Memo1.Lines.Add ("j До -" + IntToStr (j));
Self.ex.Proc(j);
Self.Memo1.Lines.Add ("j Після -" + IntToStr (j));
end;

procedure TForm1.Button5Click(Sender: TObject);
var
j : integer;
begin
j := 20;
Self.Memo1.Lines.Add ("j До -" + IntToStr (j));
Self.Memo1.Lines.Add ("Результат -" + IntToStr (Self.ex.Func (j)));
Self.Memo1.Lines.Add ("j Після -" + IntToStr (j));
end;

procedure TForm1.Button6Click(Sender: TObject);
begin
Self.Memo1.Lines.Add(IntToStr(Self.ex.GetF));
end;

procedure TForm1.Button7Click(Sender: TObject);
begin
Self.ex.SetF(Self.ex.GetF + 1);
end;


Те ж саме, що і в С + +, і працює так само. Що й було потрібно. І додамо кнопку для функції, яка приймає і повертає структури. Ось її обробник:

procedure TForm1.Button8Click(Sender: TObject);
var
s1 : TRec1;
s2 : PRec2;
begin
/ / Тут компілятор буде лаятися, але в даному
/ / Випадку це не важливо. Просто подивимося, що
/ / До ініціалізації s2 – це будь-яка нісенітниця …
Self.Memo1.Lines.Add ("s2 до:" + # 9 +
IntToStr(s2.n2) + #9 +
IntToStr(s2.a[0]) + #9 +
IntToStr(s2.a[1]) + #9 +
IntToStr(s2.a[2]) );

/ / Ініціалізація s1
s1.n := 10;
s1.i := 1;
s1.j := 2;
s1.k := 3;

/ / Якщо функція повертає вказівник на запис (структуру) –
/ / Треба підготувати покажчик. Це вам не клас.
/ / S2 – типу PRec2, а не TRec2
s2 := Self.ex.Struct1to2(s1);

/ / … а потім – те, що ми вимагали.
Self.Memo1.Lines.Add ("s2 після:" + # 9 +
IntToStr(s2.n2) + #9 +
IntToStr(s2.a[0]) + #9 +
IntToStr(s2.a[1]) + #9 +
IntToStr(s2.a[2]) );
end;


Що робить функція – зрозуміло, тут інша тонкість. Конструктор повертає (в коді на С + +) покажчик на клас, а ми присвоюємо повертається значення змінної, яка, начебто, не покажчик. Struct1to2 теж повертає покажчик – і його треба підготувати. Це пояснюється в [3]: "Об'єкт – це динамічний екземпляр класу. Об'єкт завжди створюється динамічно, в" купі ", тому посилання на об'єкт фактично є покажчиком (але при цьому не вимагає звичайного оператора разименованія "^"). Коли ви привласнюєте змінної посилання на об'єкт, Delphi копіює тільки покажчик, а не весь об'єкт. Використовуваний об'єкт повинен бути звільнений явно. "


А в С + + структура відрізняється від класу трохи менше, і робота з ними майже однакова.

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


Зрозуміло, можна винести опис абстрактного класу, оголошення експортованих функцій, використовуваних типів тощо в окремий модуль. Можливо, це краще, ніж запихати всі в один файл. За Принаймні, я так і роблю. Але це вже деталі, що стосуються стилю, а не функціональності.


6. Висновок


При такому підході не можна звертатися до полів даних безпосередньо. Хоча, це не проблема, можна використовувати функції-зчитувачі і установники. Не можна використовувати закриті функції. А навіщо їх використовувати? Для використання є відкриті. Ті, хто знайомі з моделлю СОМ, можуть сказати, що це перекручений варіант нормальної технології. Але для створення повноцінного СОМ-сервера потрібно дещо більше знань. Показаний спосіб дозволяє використовувати відкриті методи класу, не вдаючись у подробиці реалізації класу і не витрачаючи багато часу. Це дає можливість швидко отримати працездатний варіант програми, і вже потім доводити її до пуття. А то й просто отримати задовільний результат.


7. Використана література



  1. Круглінскі Д., Уінгоу С., Шеферд Дж.
    Програмування на Microsoft Visual C + + 6.0 для професіоналів / Пер. з англ. – СПб: Пітер; М.: Видавничо-торговий дім "Російська редакція", 2001 – 864 с.: Іл.
  2. Кент М.
    Delphi 6 для професіоналів (+ СD). – Пітер, 2002. – 1088с.: Іл.
  3. Лішнер Р.
    Delphi. Довідник. – Пер. з англ. – СПб: Символ-Плюс, 2001. – 640 с., Іл.
  4. С. Тейксейра, К. Пачеко
    Delphi 5. Керівництво розробника.
  5. Озеров В.
    Delphi. Поради програмістів. – СПб: Символ-Плюс, 2002.-912 с., Іл.

До матеріалу додаються файли:


  • Проекти, які використовуються в якості прикладу (276 K) оновлення від 8/15/2006 4:15:00 AM

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*