С использованием стандартной библиотеки C++ будет выглядеть примерно так:
Код:
#include <string>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <list>
using namespace std;
class March
{
public:
// деструктор ~March(), который создаётся автоматически
// вполне нас устраивает
// автоматический конструктор копирования (почленное копирование) тоже годится.
// автоматический оператор присваивания также должен работать почленно.
March() : _number(unsigned()) {}
// нужно для использования контейнеров
March(string const& begin, string const& end, unsigned number) :
_begin(begin), _end(end), _number(number)
{
}
// оператор << лучше сделать внешним из логических соображений:
// он равнозначно относится и в `a` и к `b`
friend bool operator<(March const& a, March const& b)
{
return a._number < b._number;
}
friend ostream& operator<<(ostream& os, March const& a)
{
return os << __tag_begin << "\t" << a._begin << "\n"
<< __tag_end << "\t" << a._end << "\n"
<< __tag_number << "\t" << a._number << "\n";
}
friend istream& operator>>(istream& is, March& a)
{
string tag;
// в случае неудачного чтения из потока (а именно несовпадения названий тэгов)
// помечаем поток неверным (и прерываем дальнейшее его использование)
// все остальные проверки нужны чтобы вовремя остановиться.
if (!(is >> tag) || (tag!=__tag_begin) || !getline(is, a._begin) ||
!(is >> tag) || (tag!=__tag_end) || !getline(is, a._end) ||
!(is >> tag) || (tag!=__tag_number) || !getline(is, a._number))
{
is.setstate(ios::failbit);
}
// здесь скрыта потенциальная угроза порчи данных в исключительной ситуации:
// когда класс March будет недопрочитан (например неправильный формат файла)
// непрочитанные поля сохранят значения, с которыми они зашли в оператор чтения
// из потока. Чтобы этого не случилось, желательно копировать элементы в контейнер,
// а не читать прямо внутри его. Пример будет ниже.
return is;
}
// публикуем для поиска
unsigned number() const {return _number;}
private:
string _begin, _end;
unsigned _number;
// так мы позволим менять форму ввода-вывода
static string const __tag_begin, __tag_end, __tag_number;
};
string const March::__tag_begin("begin:"), March::__tag_end("end:"), March::__tag_number("number:");
void main()
{
// в данном случае 2-связный список предпочтительней вектора
// потому что класс March тяжело копируется (содержит 2 динамические строки)
// для сортировки желательно использовать список либо вектор указателей.
// Создаём список из 8 одинаковых элементов.
list<March> marchs(8, March(string(), string(), 0U));
{
ifstream fin("data.txt");
// загружаем данные от начала до конца списка
for(list<March>::iterator out_first=marchs.begin(), out_last=marchs.end();
out_first!=out_last && (fin>>*out_first); ++out_first)
{
// пример уязвимости при чтении: если при чтении 4-го элемента произойдёт сбой,
// он останется недопрочитанным (сохранит значения, в данном случае присвоенные
// по умолчанию).
}
}
marchs.sort();
// класс-функтор для поиска по номеру маршрута
struct have_number_t
{
unsigned arg;
have_number_t(unsigned arg1) : arg(arg1) {}
bool operator()(March& a) {return a.number()==arg;}
}
cout << "Номер маршрута? " << flush;
have_number_t search_number;
cin >> search_number.arg;
// поиск c использованием search_number.operator()();
list<March>::iterator found=find_if(marchs.begin(), marchs.end(), search_number);
if (found!=marchs.end())
{
cout << *found << "\n";
}
else
{
cout << "Не найдено\n";
}
}
Но если использовать map<unsigned,March>, то March::operator<() не нужен, и всё намного проще:
Код:
#include <map>
//using namespace std;
void main()
{
map<unsigned,March> march_by_number;
// класс map сразу использует сортированный список (точнее дерево)
{
ifstream fin("data.txt");
// собираем вместе аргумент для map<unsigned,March>::insert()
// на самом деле map<unsigned,March> хранит элементы не March,
// а pair<unsigned,March>. Соответсвенно итератор контейнера map
// будет указывать на элементы этого типа pair<...>. Первый в паре
// будет ключом для поиска, второй - соотвествующим March.
pair<unsigned,March> blank_march;
for(unsigned n=0; n<8 && fin>>blank_march.second; ++n)
{
// готовим ключ для поиска
blank_march.first = blank_march.second.number();
// добавляем в сортированный список через копирование.
// если произойдёт сбой, недочитанный blank_march
// не будет помещён в контейнер. Таким образом,
// march_by_number содержит только целостные данные.
march_by_number.insert(blank_march);
}
}
cout << "Номер маршрута? " << flush;
unsigned search_number;
cin >> search_number.arg;
map<March>::iterator found=march_by_number.find(search_number);
// можно было бы использовать map<...>::operator[](), но он добавляет
// новые элементы, если таких не было раньше. Нам этого не нужно.
if (found!=marchs.end())
{
// помним что итератор указывает на пару
cout << found->second << "\n";
}
else
{
cout << "Не найдено\n";
}
}