БлогNot. Редактируемый список чего-то в C++ Builder
Редактируемый список чего-то в C++ Builder
Решим типовую задачу поддержки редактируемого списка (таблицы) на основе табличной компоненты TStringGrid из библиотеки VCL. Допустим, нам нужен список со столбцами «Фамилия» (строка до 30 символов длиной) и «Показатель» (целое или вещественное число до 8 символов длиной).
Для работы с данными добавим на форму компоненту StringGrid1 с вкладки Additional , установим ей в Инспекторе Объектов следующие свойства:
Справа от таблицы расположим Panel1 и изменим ему свойства:
У самой формы можно поставить
Визуально «подровняем» таблицу и панель инструментов, добавим на панель кнопки «Добавить» и «Удалить», выйдет примерно вот что (верх окна):
В Unit1.cpp (например, перед конструктором формы) опишем глобальные данные:
На событие OnResize формы выполним код:
Panel1->ClientW >ClientW >ClientWidth — Panel1->ClientWidth; StringGrid1->Left = 0; Panel1->Left = StringGrid1->ClientWidth+1; Panel1->Top = 0; StringGrid1->Top = 0; Panel1->ClientHeight = Form1->ClientHeight; StringGrid1->ClientHeight = Form1->ClientHeight; StringGrid1->ClientW >ClientWidth — Panel1->ClientWidth; StringGrid1->ColW >ColW >ClientWidth — StringGrid1->ColW >
30.09.2014, 17:57; рейтинг: 12641
Итак, в С++ Builder строки можно задать тремя разными способами: через специальный класс AnsiString, через массив символов char[] и через указатель на первый символ массива char*:
| ‘ > ; char String4 [ 7 ] = < 'H' , 'e' , 'l' , 'l' , 'o' , '!' , ' ' >; |
Задание строк String3 и String4 на самом деле эквивалентно, просто если задавать строку так, как у нас задана строка String3, то её обязательно нужно сразу инициализировать, тогда циферку в квадратные скобки за вас вставит компилятор.
Использование класса AnsiString удобно тем, что заданные с его помощью строки являются динамическими и можно особо не париться с их размером. То есть можно писать, например, так:
AnsiString String1; String1=»Hello!»; String1=»Hello world!»;
И пофигу, что во втором случае строка стала длиннее, пускай Builder сам с этим разбирается.
Для символьных массивов такой фокус не прокатит от слова совсем. Для указателя прокатит, но нужно понимать, что при этом на самом деле не меняется исходный массив, а просто изменяется значение указателя и он начинает указывать на другой существующий массив (который кто-то создал, память под него выделил). При этом старый массив, на который указывал указатель, просто потеряется. Это чревато всякими утечками памяти и тому подобными косяками.
Строки, заданные как массив символов, должны оканчиваться нулём (по-другому их называют нультерминированные). Массив Char-ов может содержать нули, но при этом строкой считается всё, что содержится от начала массива до первого встреченного нулевого символа. Например, можно задать вот такой массив:
Если в этом случае попробовать посчитать длину строки TempStr, то она окажется равной пяти, то есть количеству элементов от начала строки до нультерминатора, без учёта его самого. (Как видите, элементы массива не обязательно писать в кавычках, как символы, можно и напрямую в hex).
Более того, один массив может содержать сразу две строки. Фактически в предыдущем примере так оно и есть. Если мы напишем вот такой код:
| ‘ > ; Edit1 — > Text = TempStr ; Edit2 — > Text = & TempStr [ 6 ] ; // &TempStr[6] — адрес шестого элемента (‘w’) |
, то в Edit1 получим текст «Hello», а в Edit2 — текст «world».
Ещё одной фишкой является то, что в AnsiString элементы считаются с единицы, а в массивах символов (да и в любых вообще массивах) — с нуля (именно поэтому символ ‘w’ в предыдущем примере шестой, а не седьмой). То есть вот такой код:
| ‘ > ; AnsiString AStr = «Hello» ; Edit1 — > Text = Str [ 1 ] ; Edit2 — > Text = AStr [ 1 ] ; |
char Str[]=<'H','e','l','l','o',' '>; AnsiString AStr=»Hello»; Edit1->Text=Str[1]; Edit2->Text=AStr[1];
выведет в поле Edit1 символ ‘e’, а в поле Edit2 символ ‘H’.
Да, кстати, как видите, элементы AnsiString тоже можно получить по индексам, совсем как элементы массива.
Поскольку AnsiString — это класс, то он имеет множество всяких полезных свойств и методов, позволяющих особо не париться с различными операциями над строками. Для массива вместо этого приходится пользоваться специальными библиотечными функциями или писать свои.
Как правило, самый частый вопрос, который возникает при работе со строками, — это преобразование типов, когда из AnsiString нужно получить массив Char или наоборот.
Итак, сначала получаем из AnsiString массив Char. Сделать это можно следующим образом:
AnsiString AStr=»Hello»; char Str1[10]=<'a','a','a','a','a','a','a','a','a','a'>; strcpy(Str1,AStr.c_str()); // копируем в существующий массив Edit1->Text=Str1;
AnsiString AStr=»Hello»; char *Str2; // создаём указатель, который пока ни к чему не привязан Str2 = new char[ Astr.Length() + 1 ]; // создаём новый массив, // привязываем к нему указатель strcpy(Str2,AStr.c_str()); // и копируем в этот массив Edit1->Text=Str2;
В первом случае нужно дополнительно проверить, чтобы массив, в который происходит копирование, был достаточного размера. Размер массива должен быть на 1 больше количества символов в строке (1 дополнительный элемент нужен для нультерминатора).
Как делать не нужно!
AnsiString AStr=»Hello»; char *Str2 = AStr.c_str();
В хэлповнике по Builder-у написано, что функция c_str() возвращает временный указатель на внутренний буфер строки в объекте AnsiString, который действителен только на время выполнения выражения, в котором он используется.
Для того, чтобы преобразовать массив char-ов в AnsiString можно использовать вот такой метод:
| ‘ > ; AnsiString AStr ; AStr = AnsiString ( Str1 ) ; |
char Str1[]=<'H','e','l','l','o',' '>; AnsiString AStr; AStr=AnsiString(Str1);
Преобразование выполняется до первого встреченного нультерминатора. То есть, если массив выглядел, скажем, вот так: <‘H’,’e’,’