Модуль для роботи з асоціативними масивами в C + +
Builder h2>
Заєв А.А. p>
Вступ h2>
Мій улюблений мова - PHP. Він витончений і простий, але, на
жаль, призначений тільки для програмування сайтів. «Звичайне» програму
на ньому не напишеш. p>
На щастя, деякі технології, реалізовані в PHP
можна перенести і в інші мови програмування: наприклад, в C + +. p>
Одна з таких технологій - асоціативні масиви. p>
В асоціативному масиві замість числових індексів
використовуються ключі будь-яких типів. Дані в асоціативному масиві так само можуть
бути різнотипними. p>
Наприклад: p>
ass_arr array; p>
array [0] = 123; p>
array [ "name"]
= "John Silver"; p>
Тут в масиві array створюються два елементи, один з
яких має ключ «0» і числове значення «123», інший - ключ «name» і
строкове значення «John Silver». «Ass_arr» - не масив дуп, як подумало
більшість читачів, а можливе ім'я типу (класу) асоціативного масиву. p>
Зручно? Зручно! Не треба описувати що входять в масив
елементи та їх типи. Не потрібно думати про розмір масиву - він динамічний. Не потрібно
дбає ні про що, крім вільної пам'яті. p>
Детальніше про зручності p>
Асоціативний масив - всього лише спосіб представлення
даних. Будь-яка завдання, яке вирішується за допомогою асоціативних масивів, може бути
вирішена за допомогою структур або класів. Однак, використання асоціативності
істотно спрощує вирішення багатьох завдань. p>
Розглянемо простий приклад. Візьмемо структуру, в
якій зберігаються налаштування якоїсь програми. Опишемо її так: p>
struct preferences p>
( p>
int WindowWidth; p>
int WindowHeight; p>
int WindowX; p>
int WindowY; p>
char documentPath [128]; p>
); p>
Для збереження даних цієї структури де-небудь,
буде потрібно спеціальна функція, яка буде «знати» всі поля, які
присутні в цій структурі. Наприклад, така: p>
bool
savePreferences (struct preferences * pref) p>
( p>
saveInteger (pref-> WindowWidth); p>
saveInteger (pref-> WindowHeight); p>
... p>
saveString (pref-> documentPath); p>
) p>
При додаванні в структуру нового поля, доведеться
доповнювати цю функцію. p>
Якщо ж замість змінної такої структури
використовувати асоціативний масив - все що потрібно функції збереження --
перед початком роботи сформувати список ключів цього масиву і в циклі по
списку ключів, зберегти кожен елемент, грунтуючись на його тип. p>
Це могло б виглядати так: p>
bool
savePreferences (ass_arr * pref) p>
( p>
int i; p>
Variant v; p>
// цикл по всіх елементів p>
for (i = 0; i <
pref-> Count (); i ++) p>
( p>
// витягаємо черговий елемент p>
v = (* pref) [pref-> key (i)]. v () p>
// якщо елемент числового типу, p>
// зберігаємо його числове значення p>
if (VarType (v) ==
varInteger) p>
( p>
saveInteger ((* pref) [pref-> key (i)]. asInteger ());
p>
) p>
// далі для інших типів p>
... p>
) p>
) p>
Як бути, якщо потрібно заповнити даними налаштувань
Builder'овскую форму? Потрібно буде ще одна функція. При використанні
асоціативних масивів цю процедуру можна автоматизувати. p>
А головне: при додаванні в масив установки нового
поля - не потрібно нічого змінювати. p>
Існує ще багато подібних завдань. Асоціативні
масиви - універсальний засіб. Але як реалізувати їх в C ++? p>
Реалізація асоціативних масивів в C + + Builder h2>
Для реалізації класу асоціативного масиву, я
використовував кілька стандартних класів: по-перше, Variant - мультітіп. У
змінної типу Variant може зберігається значення будь-якого зі стандартних типів.
По-друге, CList - для створення внутрішніх списків. Тому, поза Builder'а --
наприклад, в MSVC + +, цей клас працювати не буде. Однак, при великому бажанні,
його можна перенести (використавши list з stl і написав свою реалізацію Variant).
p>
Моя бібліотека містить три класи: ass_arrEl - клас
елемента масиву, ass_arr - клас простого асоціативного масиву, і його
спадкоємець - prop_ass_arr, призначений для роботи з вікнами налаштування. Він
«Вміє» зберігати і завантажувати свій вміст з реєстру, заповнювати їм форми і
заповнюватися вмістом форми сам. p>
Як працювати з моїми класами p>
Кілька наочних прикладів: p>
Простий масив. Робота зі значеннями. p>
# include
"ass_arr.h"; p>
ass_arr a; p>
// так можна створити елементи p>
a [ "name"] = "Сажин"; p>
a [ "surname"] = "Біснуватий"; p>
// а так - звернутися до їх значень p>
ShowMessage (a [ "name"]. v ()); p>
ShowMessage (a [ "name"]. v ()); p>
a [ "name"]. v () повертає значення типу
Variant. P>
Робота з ключами p>
# include
"ass_arr.h"; p>
ass_arr a; p>
int i; p>
// Створюємо два значення p>
a [ "name"] = "Сажин"; p>
a [ "surname"] = "Біснуватий"; p>
// Виводимо їх у циклі p>
for (i = 0; i <
a.Count (); i ++) p>
( p>
// a.key (i) повертає ключ i-го по рахунку елементу. p>
// Ключ теж типу Variant. Зауважте, що при виведенні я
напряму p>
// не вказую ключів: вони визначаються автоматично p>
ShowMessage (a [a.key (i)]. v ()); p>
) p>
У ключах не існує неприпустимих символів. Ви
можете використовувати в якості ключів навіть імена файлів з повними шляхами! p>
Вкладені масиви. Найпростіше дерево. p>
# include "ass_arr.h"; p>
ass_arr a; p>
ass_arr * inner; p>
int i; p>
// створюємо новий асоціативний масив p>
inner = new ass_arr; p>
// заповнюємо його даними. (* inner) [] - звернення до
операторові p>
// об'єкта за вказівником. p>
(* inner) [ "name"]
= "Федір"; p>
(* inner) [ "surname"]
= "Сумкін"; p>
// вносимо його в нульовий елемент масиву a p>
a [0] = inner; p>
inner = new
ass_arr; p>
(* inner) [ "name"]
= "Федір"; p>
(* inner) [ "surname"]
= "Чистяков"; p>
a [1] = inner; p>
inner = new
ass_arr; p>
(* inner) [ "name"]
= "Федір"; p>
(* inner) [ "surname"]
= "Беззвестний"; p>
// привласнювати можна посилання на масив, або ж сам
масив p>
a [2] = * inner; p>
// тепер виведемо поле surname другого елементу p>
inner = a [1]. sub ();// заносимо в inner посилання на
вкладений масив другого елементу p>
ShowMessage ((* inner) [ "surname"]); p>
// виведемо поле name третьому елементу (можна писати
так) p>
ShowMessage ((* (a [2 ]))[" name "]); p>
Вкладені масиви так само можуть мати вкладені
масиви. Подібні структури, по суті, являють собою дерева з вузлами
довільної структури. p>
Заповнення форми значеннями масиву. Завантаження значень
асоціативного масиву. Збереження асоціативного масиву в реєстрі та завантаження
його з реєстру. p>
Припустимо, на формі mainForm два поля: TEdit login і
TEdit password. Крім того, в масиві конфігурації необхідно зберігати число
запусків програми (numStarts). p>
# include
"ass_arr.h"; p>
prop_ass_arr
config; p>
... mainForm:: onCreate (...) p>
( p>
// завантажуємо конфігурацію з реєстру p>
if
(! config.loadSection (HKEY_CURRENT_USER, "Software/Kuu/Passworder ")) p>
ShowMessage ( "Не вдалося завантажити конфігурацію з
реєстру "); p>
config [ "numStarts"]. v () = config [ "numStarts"]. v () +1; p>
) p>
... mainForm:: onShow (...) p>
( p>
// заповнюємо форму значеннями конфігурації p>
config.toForm (this);
p>
) p>
...
mainForm:: onDestroy (...) p>
( p>
// заповнюємо конфігурацію значеннями з форми p>
config.fromForm (this); p>
if
(! config.saveSection (HKEY_CURRENT_USER, "Software/Kuu/Passworder ")) p>
ShowMessage ( "Не вдалося зберегти конфігурацію в
реєстр "); p>
) p>
Так просто? Так! p>
saveSection і loadSection підтримують вкладені
масиви необмеженого рівня вкладеності. p>
Віктор Соколов p>
http://kuu.spb.ru p>
http://www.realcoding.net/ p>
Виключення в Borland С + + Builder 6.0 p>
У статті розглядаються проблеми, що виникають при
роботі з винятками в середовищі Borland C + + Builder 6.0 p>
Я не спроста уточнив, що всі нижчевикладене відноситься
в першу чергу до шостої версії середовища, оскільки я натрапив на ці проблеми
саме в ній, і не перевіряв інші версії. p>
Отже, короткий інструктаж із застосування винятків,
згідно з книг, статей та офіційним джерел. p>
Конструкція виключень має такий вигляд. p>
Приклад № 1 p>
try// try - вказує на те, що піде далі блок
винятків p>
( p>
throw 1;// throw - ключове слово, власне і
що створює виключення p>
) p>
catch (int a)// catch - вказує на те, що далі
піде блок вилову винятків p>
( p>
MessageDlg ( "Exception - 1", mtError, TMsgDlgButtons ()
<
); p>
Тепер, розберемо цю конструкцію детальніше. p>
try p>
Ключове слово, яке вказує на початок блоку
винятків. Використовується тільки в С + +, а в С доведеться використовувати __try.
Однак, __try можна так само використовувати в C ++. p>
Блок винятків полягає у фігурні дужки, як це
показано вище, і при необхідності, в ньому створюється виняток. p>
throw p>
Ключове слово, що створює виняток. Іншими словами,
при створенні виключення, throw ініціалізує тимчасовий об'єкт того типу, з
допомогою якого ми хочемо створити виняток. У наведеному прикладі, ми
використовуємо тип int, відповідно, створюється тимчасовий об'єкт типу int,
що містить дані - 1. p>
catch p>
Ключове слово, яке вказує на початок блоку обробки
винятків. Тут ми можемо розмістити будь-які реакції на спіймані виключення. У
параметрах блоку ми повинні вказати тип даних, використаний при створенні
винятків. Оскільки в наведеному вище прикладі ми створювали винятків типу
int, то і слухати нам треба на предмет типу int. Так що ми вказуємо цей тип в
дужках. p>
І начебто все! Створюємо новий проект, поміщаємо на
форму кнопочку, і вставляємо в обробник натискання цієї кнопки наш приклад. Потім
запускаємо. Компілятор впевнено зчитує рядки. Лінкер не бачить нічого
підозрілого. Програма запускається, і радісно чекає початку роботи. Ми
натискаємо на кнопочку ... p>
Бах !!! p>
І добре, якщо вам скажуть, що «Project XXX.exe raised exception class int with
message 'Exception Object Address: 0x8E4C6E'. process stopped. use Step or Run
to Continue. " p>
У деяких випадках вас взагалі можуть викинути з
середовища, включаючи повне зависання всій Windows. p>
Отже, що ж неправильно в цьому коді? p>
По-перше, давайте розберемося, що таке «Project
XXX.exe raised exception ... » p>
Це повідомлення з'являється тоді, коли програма, над
якій працює дебаггер, створює виняток. Проблема в тому, що С + + Builder
(так само як і Delphi) чомусь всі винятки, які знаходить, за замовчуванням
скидає на стандартний обробник. p>
Однак, подібне повідомлення ми можемо побачити не тільки
при обробці свого виключення. Це повідомлення виникає також у випадках, якщо
це Operating System Exception (Виняток операційної Системи) або Language
Exception (Виняток Мови). p>
У даному прикладі, ми працюємо з допомогою засобів мови,
аніскільки не чіпаючи (ну хіба що зовсім небагато) операційну систему. p>
Так що варто сходити в гості до дебаггер, і перевірити
- Чим він там займається. P>
Знайти його можна за адресою: Tools-> Debugger Options p>
Тут нас цікавить вкладка Language Exceptions.
Заходимо всередину, і що ми бачимо? У наявності суворе порушення правил безпеки!
Виявляється, дебаггер успішно ігнорує виключення, що належать Delphi,
Microsoft, VisiBroker, CORBA! Але навіщо? Відповідь очевидна - кожен з цих
шедеврів думки має свої власні обробники винятків, які заточені
під специфічні потреби. І вони зовсім не збираються користуватися стандартним
обробником. p>
Ми, звичайно, можемо вписати сюди тип int, але в цьому
випадку дебаггер буде ігнорувати все, що пов'язане з цим типом даних. Це не
є правильно. p>
Раз сильні світу цього реєструють тут свої типи
даних, для коректної обробки, значить, ми просто зобов'язані створити свій тип
даних, що відповідає за виключення. p>
Назвемо його TCustomException. Кількома по Add, і
вписуємо це в рядок діалогу. Потім клацаємо OK. Все, вітаю пані та
панове! Ми успішно зареєстрували тип даних. Тобто тепер дебаггер НЕ
буде ображатися на те, що ми використовуємо для створення виключень щось, про що
він не знає. p>
Отже, йдемо з опцій дебаггер, і повертаємося в
програму. p>
Змінимо код. p>
Приклад № 2 p>
сlass TCustomException ();// Наш тип даних p>
TCustomException
NewEx;// Об'єкт класу (типу) TCustomException p>
try// Початок блоку створення винятків p>
( p>
throw NewEx;// Створення виключення p>
) p>
catch (TCustomException)// Початок блоку вилову
винятків p>
( p>
MessageDlg ( "Exception !!!", mtError,
TMsgDlgButtons () <
); p>
Оскільки класи є типами даних, ми легко
створюємо свій TCustomException. Однак, незручно створювати під кожне нове
виняток - новий клас. Так що краще створити об'єкт класу TCustomException,
що ми і робимо. Створюємо об'єкт NewEx, і використовуємо його для створення
виключення, яке потім з успіхом відловлюється. В іншому конструкція
працює так само, як вищеописана, з тією різницею, що вона працює! p>
Однак, подібна конструкція теж має проблеми. У
випадку більш-менш складної ситуації, нам доведеться вибудовувати цілі ієрархії
винятків. Щоб цього не допускати, краще користуватися винятками з
параметрами. p>
Приклад № 3 p>
// Клас для винятків з параметрами. p>
// Не забувати реєструвати його в опціях дебаггер! p>
class TEx p>
( p>
public: p>
int fCode; p>
TEx (int
eCode) (fCode = eCode ;}; p>
); p>
// Код, вбудований в клавішу Button1 p>
try p>
( p>
throw TEx (1301); p>
) p>
catch (TEx Ex) p>
( p>
if (Ex.fCode == 1301) ( p>
MessageDlg ( "Exception!",
mtError, mbOKCancel, 0); p>
); p>
) p>
Іншими словами, можна всередині робочого алгоритму
створювати виключення в будь-якій невподобаний вам ситуації, а потім у
обробнику винятків, вирішувати - що робити в кожному конкретному випадку. Просто,
зручно та надійно. Які милі люди працюють в Borland! p>
Сподіваюся, ця стаття допоможе зацікавленим в їх
нерівному бою з чудовим (так, я насправді так вважаю!) інструментом --
Borland C + + Builder 6.0 p>
Заєв А.А. p>
Список літератури h2>
Для підготовки даної роботи були використані
матеріали з сайту http://www.realcoding.net/
p>