Класи QString та QVariant

Рядки використовуються практично у всіх програмах анітрохи не рідше за інші типи.

Мова C++ надає два типи рядків: традиційні рядки мови C - масиви символів, що завершуються символом '\0' та клас string. Qt надає набагато потужніший клас QString. Він призначений для зберігання рядків із 16-ти бітними символами Unicode. Unicode містить набори символів ASCII і Latin-1 зі своїми звичайними числовими значеннями. Але оскільки кожен символ в QString представлений 16 бітами, він може містити тисячі інших символів. Додаткову інформацію про Unicode ви знайдете у Розділі 15.

Конкатенація двох рядків QString може виконуватись двомісним оператором "+" або оператором "+=". Нижче наведено приклад використання обох операторів:

Крім того, є функція QString::append(), яка ідентична за своєю дією оператору "+=": І зовсім інший підхід до об'єднання рядків полягає у використанні функції QString::sprintf(): Вона підтримує той же набір специфікаторів формату, що та бібліотечна функція sprintf(). У прикладі вище, рядок str буде записаний рядок "perfect competition 100.0%".

Ще один спосіб "складання" рядка з інших рядків і чисел - використовувати arg():

У цьому прикладі "%1" буде замінено словом "permissive", "%2" - "society", "%3" - "1950" та "%4" - "1970". В результаті вийде рядок "permissive society (1950-1970)". Клас має кілька перевантажених функцій arg() обробки різних типів даних. Деякі з них мають додаткові параметри, що управляють довжиною вихідного рядка, базою системи числення та точністю представлення чисел з плавною точкою. У більшості випадків arg() представляє краще рішення, ніж sprintf(), тому що вона безпечніша,повністю підтримує Unicode і дозволяє перекладачам змінювати порядок дотримання параметрів %n .

QString дозволяє перетворювати числа в їх рядкове представлення за допомогою статичної функції QString::number():

або за допомогою QString::setNum(): Зворотне перетворення може бути виконане функціями toInt(), toLongLong(), toDouble() і т.д., наприклад: Ці функції можуть приймати необов'язковий аргумент типу bool, в якому повертається ознака успіху перетворення . Якщо перетворення може бути виконано, вони завжди повертають 0.

Найчастіше виникає ситуація, коли необхідно витягти частину рядка. Функція mid() повертає підрядок заданої довжини, починаючи із заданої позиції у вихідному рядку. Наприклад, наступний код виводить рядок "pays":

Якщо опустити другий аргумент (або передати як другий аргумент число -1), функція поверне підрядок, починаючи з заданої позиції до кінця вихідного рядка. Наприклад, наступний код виведе рядок "pays principle": Додатково є функції left() та right(). Вони обидві приймають кількість символів n і повертають перші або останні символи n вихідного рядка, відповідно. Наприклад, наступний код виведе рядок "polluter principle": Якщо потрібно виконати перевірку - чи починається чи закінчується рядок певною комбінацією символів, для цих цілей існують функції startsWith() та endsWith(): Це набагато швидше і простіше, ніж: Оператор порівняння рядків "==" чутливий до регістру символів. Для виконання реєстронезалежного порівняння, можна скористатися функціями upper() або lower(), наприклад: Для заміни одного підрядка в рядку іншим підрядком, використовуйте функцію replace(): в результаті вийде рядок "a cloudy day". Та ж сама дія може виконана за допомогоюфункцій remove() і insert(): У першому рядку видаляється п'ять символів, починаючи з 2-ї позиції, в результаті виходить рядок "a day" (з двома пробілами), потім у другу позицію вставляється слово "cloudy".

Існують перевантажені версії функції replace(), які замінюють всі входження першого аргументу на другий. Наприклад, щоб замінити всі символи '&' у рядку на "&":

Дуже часто виникає необхідність викинути з початку та кінця рядка всі зайві пробілові символи (такі як пробіли, символи табуляції, символи перекладу рядка). Для цієї мети існує функція stripWhiteSpace(): Рядок str може бути зображений як:

Рядки можуть бути розбиті на підрядки за допомогою функції QStringList::split(): У цьому прикладі рядок "polluter pays principle" розбивається на три підрядки; "polluter", "pays" та "principle". Функція split() може приймати третій необов'язковий параметр типу bool, який визначає - чи повинні ігноруватися порожні підрядки (за замовчуванням) чи ні.

Елементи списку QStringList можуть бути об'єднані в один рядок за допомогою функції join(). Як аргумент їй передається рядок, який має бути вставлений між рядками, що об'єднуються. Наприклад, наступний код демонструє, як можна об'єднати всі рядки у списку, відсортованому за алфавітом, у єдиний рядок, причому підрядки відокремлюються один від одного символом перекладу рядка:

Ще одна важлива операція над рядками - визначення довжини рядка. Для цього призначена функція length() і, як варіант, isEmpty(), яка повертає true, якщо довжина рядка дорівнює 0.

QString розрізняє порожні рядки та неіснуючі (NULL) рядки. Ці відмінності корінням сягають мови програмування C. Щоб перевірити -- чи існує рядок, можна викликатифункцію isNull(). Для більшості програм дуже важливо знати - чи містить рядок хоча б один символ. Функція isEmpty() поверне true, якщо рядок не містить жодного символу (порожній чи неіснуючий рядок).

Перетворення, між const char * і QString, здебільшого виконуються автоматично:

Цей код додає рядок типу const char * до рядка типу QString.

У деяких ситуаціях виникає необхідність явно виконувати перетворення між const char* та QString. Щоб перетворити рядок QString на const char *, використовуйте функцію ascii() або latin1(). Зворотне перетворення може бути виконане з допомогою операції наведення типу.

Коли викликаються функції ascii() або latin1(), або коли виконується автоматичне перетворення до типу const char *, рядок, що повертається, належить об'єкту QString. Це означає, що нас не повинна турбувати проблема витоку пам'яті - Qt самостійно утилізує пам'ять у міру необхідності. З іншого боку, необхідно виявляти велику обережність під час роботи з покажчиками. Наприклад, якщо оригінальна версія рядка QString буде змінена, раніше отриманий покажчик на const char * може виявитися неприпустимим. Якщо необхідно зберегти попередній варіант рядка, то для цих цілей можна скористатися послугами класу QByteArray або QCString. Вони зберігають повну копію даних.

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

Qt використовує цей метод оптимізації та інших класів, включаючи: QBrush, QFont, QPen, QPixmap, QMap , QValueList і QValueVector . Що підвищує ефективність передачі екземплярів класів зазначення, як у вигляді аргументів функцій, так і у вигляді значень, що повертаються.

C++ - це строго типізована мова, однак, іноді виникає необхідність зберігати дані у більш загальному вигляді. Найпростіший спосіб - використовувати рядки. Наприклад, рядки можуть зберігати текстові чи числові дані. Qt надає більш простий спосіб роботи зі змінними - клас QVariant.

Клас QVariant може зберігати значення багатьох типів Qt: QBrush, QColor, QCursor, QDateTime, QFont, QKeySequence, QPalette, QPen, QPixmap, QPoint, QRect, QRegion, QSize і QString. Він також може зберігати контейнери: QMap, QString .. Ми вже використовували QVariant, коли розробляли програму Spreadsheet, у Розділі 4, для зберігання вмісту комірки.

Одне із звичайних застосувань класу QVariant - створення словників (map), у яких як ключ використовуються рядки, а як значення - екземпляри класу QVariant. Як правило, інформація про конфігурацію програми зберігається та завантажується за допомогою QSettings, але іноді програми можуть обслуговувати налаштування безпосередньо, наприклад, зберігаючи їх у базі даних. QMap ідеально підходить у таких ситуаціях:

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

Тут у змінну str1 записується рядок "Humpty" і потім виконується надання змінної str2. З цього моменту обидві змінні вказують на ту саму структуру даних у пам'яті (типу QStringData). Разом зсимволами рядка вона зберігає лічильник посилань, який містить кількість об'єктів, що посилаються на неї. Оскільки і str1, і str2 посилаються на ті самі дані, то лічильник посилань дорівнює 2. Коли виконується зміна вмісту змінної str2, то перш за все створюється повна копія даних, таким чином, тепер str1 і str2 посилаються на різні структури і всі зміни будуть проводитися над їх власними копіями даних. Лічильник посилань змінної str1 ("Humpty") тепер став дорівнює 1 і лічильник посилань змінної str2 ("Dumpty") так само став дорівнює 1. Коли лічильник посилань дорівнює 1, це означає, що дані використовуються тільки одним об'єктом. Якщо тепер виконати модифікацію змінної str2, то ніякого копіювання проводитися вже не буде, тому що лічильник посилань дорівнює 1. Функція truncate() буде оперувати з даними, що належать змінній str2, і лічильник посилань залишиться рівним 1. стане рівним 0, це означає, що рядок "Humpty" більше не потрібний. У цьому випадку пам'ять, що раніше займається змінною str1, буде звільнено. Тепер обидві змінні посилатимуться на рядок "Dump", а лічильник посилань дорівнюватиме 2.

Створення класів, використовують оптимізацію неявного спільного використання даних, виконується досить легко. У щокварталу Qt Quarterly, у статті "Data Sharing with Class" (http://doc.trolltech.com/qq/qq02-data-sharing-with-class.html) описується - як це зробити.

Обхід у циклі елементів словника, який зберігає значення у варіантному вигляді, може виявитися не таким простим завданням, якщо деякі значення є контейнерами. Щоб перевірити тип варіантного значення, вам доведеться скористатися функцією type():

За допомогою QVariant можна створювати доситьскладні структури даних, що зберігають значення контейнерного типу: У цьому прикладі було створено словник із рядковими ключами (назва продукту) та значеннями типу double (ціна) або типу QMap. Словник верхнього рівня містить три ключі: "Orange", "Pear" та "Pineapple". Значення, пов'язані з ключем "Pear" - це словник із двома ключами ("Standard" та "Organic").

Можливість створення структур даних, подібних до цієї, може здатися дуже спокусливою, так як можна структурувати дані на свій розсуд. Але зручність QVariant – річ дорога. Для зручності читання доведеться пожертвувати швидкодією.