На этом шаге мы рассмотрим способы запоминания объектов в контейнерах.
Теперь, когда вы разобрались с соглашениями именования классов и общей схемой библиотеки, можно приступить к собственно запоминанию данных в контейнерах. Независимо от типа контейнера, важно решить, запоминать ли объекты косвенно (по указателю) или непосредственно (копированием). Контейнеры, у которых в имени содержится буква I, подобные TIQueueAsVector, запоминают объекты косвенно, в виде указателей на них. Контейнеры без I (TQueueAsVector, например) запоминают непосредственно сами объекты. В листинге 1 демонстрируется использование контейнеров обоих видов.
Листинг 1. DIRECT.CPP (контейнеры с непосредственным и косвенным запоминанием)
#include <iostream.h> #include <cstring.h> #include <classlib\arrays.h> #define TRUE 1 #define FALSE 0 // Контейнеры с непосредственным и косвенным запоминанием объектов TArrayAsVector<string> dStrings(10); TIArrayAsVector<string> iStrings(10); int main() { int done = FALSE; // Управляющая переменная цикла while int i; // Управляющая переменная цикла for string s; // Объект вводимой строки char buf[81]; // Символьный буфер ввода while (!done) { cout << " Введите строку: "; // Приглашение ввода строки cin.getline(buf, sizeof(buf)); // Записать в буфер s = buf; // Преобразовать в объект string if (s.length() == 0) // Проверить длину на нуль done = TRUE; // Если да - завершить цикл while else { //....иначе... dStrings.Add(s); // Добавить в контейнер iStrings.Add(new string(s));// Добавить в косвенный контейнер } } // Отобразить объекты контейнера с непосредственным запоминанием cout << endl; cout << " ПрЯмой массив строк: " << endl; for (i=70; i < dStrings.GetItemsInContainer(); i++) cout << dStrings[i] << endl; // Отобразить объекты контейнера с косвенным запоминанием cout << endl; cout << " Косвенный массив строк: " << endl; for (i = 0; i < iStrings.GetItemsInContainer(); i++) cout << *iStrings[i] << endl; return 0; }
В программе объявляются два контейнера, способные запоминать объекты класса string, объявление которого содержится в заголовочном файле CSTRING.H. Например, следующий оператор создает контейнер, запоминающий непосредственно сами объекты:
TArrayAsVector<string> dStrings(10);
Оператор, очень похожий на предыдущий, однако использующий другой класс, создает контейнер, который запоминает указатели на динамические строки, созданные с помощью оператора new:
TIArrayAsVector<string> iStrings(10);
Оба контейнера, dStrings и iStrings, обладают одинаковыми наборами функций и возможностями. Они различаются только тем, что в dStrings создается копия запоминаемой строки, а в iStrings запоминается указатель на строковый объект.
Далее программа запрашивает строку, которую помещает в символьный буфер. После использования буфера для создания строкового объекта s программа вставляет его в контейнер dStrings с помощью обращения к функции Add() контейнера класса:
dStrings.Add(s);
Функция Add() запоминает новый объект в массиве контейнера. Не все контейнерные классы снабжаются функцией Add(), но все контейнеры имеют как минимум один способ запоминания объектов. Для вставки строки в контейнер iString в программе используется оператор:
iStrings.Add (new string(s));
Было бы некорректно передавать адрес объекта s функции косвенного контейнера Add(). Строковая переменная s - только временный объект, который размещается в стеке программы. Передача адреса этого объекта косвенному контейнеру послужит причиной возникновения всевозможных проблем при уничтожении контейнера. Таким образом, при использовании косвенных контейнеров всегда следует знать, где размещаются ваши объекты. Неплохо, например, воспользоваться оператором создания динамических строковых объектов new для запоминания их в контейнере.
Поскольку в этом примере контейнерами служат массивы, они снабжаются функцией GetItemsInContainer(), сообщающей число сохраненных строк. В программе эта функция используется в цикле for для отображения объектов в контейнере с запоминанием непосредственно объектов:
for (i=0; i<dStrings.GetItemsInContainer(); i++)
cout << dStrings[i] << endl;
Отображение объектов в косвенном контейнере почти такое же, только необходимо разыменовывать элементы массива, поскольку все они - указатели:
for (i=0; i < iStrings.GetItemsInContainer(); i++) cout << *iStrings[i] << endl; // Обратите внимание на разыменование
На следующем шаге мы рассмотрим альтернативный доступ к объектам контейнера.