путем обхода всех сообщений и их вставки с помощью функции
insert
:
for (Mess_iter p = mfile.begin; p!=mfile.end; ++p) {
const Message& m = *p;
string s;
if (find_from_addr(&m,s))
sender.insert(make_pair(s,&m));
}
В
ассоциативный массив включаются пары (ключ, значение), созданные с помощью функции
make_pair
. Для того чтобы найти имя отправителя, используем “кустарную” функцию
find_from_addr
.
Почему мы используем ссылку
m
и передаем ее адрес? Почему не использовать итератор
p
явно и не вызвать функцию так:
find_from_addr(p,s)
? Потому что, даже если мы знаем, что итератор
Mess_iter
ссылается на объект класса
Message
, нет никакой гарантии, что он реализован как указатель.
Почему мы сначала записали объекты класса
Message
в вектор, а затем создали объект класса
multimap
? Почему сразу не включить объекты класса
Message
в ассоциативный массив класса
map
? Причина носит простой и фундаментальный характер.
• Сначала мы создаем универсальную структуру, которую можно использовать для многих вещей.
• Затем используем ее в конкретном приложении.
Таким образом, мы создаем коллекцию в той или иной степени повторно используемых компонентов. Если бы мы сразу создали ассоциативный массив в объекте класса
Mail_file
, то вынуждены были бы переопределять его каждый раз, когда хотим использовать его для решения другой задачи. В частности, наш объект класса
multimap
(многозначительно названный
sender
) упорядочен по полю
Address
. Большинство других приложений могут использовать другой критерий сортировки: по полям Return, Recipients, Copy-to fields, Subject fields, временным меткам и т.д.
Создание приложений по этапам (или слоям (layers), как их иногда называют) может значительно упростить проектирование, реализацию, документацию и эксплуатацию программ. Дело в том, что каждая часть приложения решает отдельную задачу и делает это вполне очевидным образом. С другой стороны, для того чтобы сделать все сразу, нужен большой ум. Очевидно, что извлечение информации и заголовков сообщений электронной почты — это детский пример приложения. Значение разделения задач, выделения модулей и поступательного наращивания приложения по мере увеличения масштаба приложения проявляется все более ярко.
Для того чтобы извлечь информацию, мы просто ищем все упоминания ключа "John Doe", используя функцию
equal_range
(раздел Б.4.10). Затем перемещаемся по всем элементам в последовательности
по элементам объекта класса map, мы получаем последовательность пар (ключ,значение), в которых, как в любом другом объекте класса
pair
, первый элемент (в данном случае ключ класса
stringkey
) называется
first
, а второй (в данном случае объект класса
Message
) —
second
(см. раздел 21.6).
23.4.1. Детали реализации
Очевидно, что мы должны реализовать используемые нами функции. Соблазнительно, конечно, сэкономить бумагу и спасти дерево, предоставив читателям самостоятельно решить эту задачу, но мы решили, что пример должен быть полным.
Конструктор класса
Mail_file
открывает файл и создает векторы
lines
и
m
.
Mail_file::Mail_file(const string& n)
// открывает файл с именем "n"
// считывает строки из файла "n" в вектор lines
// находит сообщения в векторе lines и помещает их в вектор m,
// для простоты предполагая, что каждое сообщение заканчивается
// строкой "––––" line
{
ifstream in(n.c_str); // открываем файл
if (!in) {
cerr << " нет " << n << '\n';
exit(1); // прекращаем выполнение программы
}
string s;
while (getline(in,s)) lines.push_back(s); // создаем вектор
// строк
Line_iter first = lines.begin; // создаем вектор сообщений
for (Line_iter p = lines.begin; p!=lines.end; ++p) {
if (*p == "––––") { // конец сообщения
m.push_back(Message(first,p));
first = p+1; // строка –––– не является частью
// сообщения
}
}
}
Обработка ошибок носит слишком элементарный характер. Если бы писали эту программу для своих друзей, то постарались бы сделать ее лучше.
ПОПРОБУЙТЕ
Что значит “более хорошая обработка ошибок”? Измените конструктор класса
Mail_file
так, чтобы он реагировал на ошибки форматирования, связанные с использованием строки “––––”.