В программировании часто возникает необходимость обрабатывать два объекта, например, простую пару чисел, как один. Такая задача стала особенно актуальной, когда в синтаксис программирования были внедрены ассоциативные массивы с их парой: название поля –> значение поля. Причём типы обоих значений в паре могут быть совершенно разными – в этом и состоит особая ценность рассматриваемой структуры и на это необходимо обратить особое внимание.
Откликом на такую задачу одновременной обработки пары значений стало появление структуры pair, описание которой приводится ниже.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
// структура шаблона хранит пары объектов типа A1 и A2; // тип fsttype совпадает с типом параметра шаблона A1, а тип sectype – A2; // таким образом, на практике A1 может соответствовать имени поля, // а A2 – его значению template <class A1, class A2> // все члены типа pair являются открытыми - объявлены как struct, а не как class struct pair { typedef fsttype A1; typedef sectype A2; A1 first; // 1-ый элемент пары fst имеет тип A1 A2 second; // 2-ой элемент пары sec – A2 // конструктор по умолчанию - инициализирует 1-ый элемент пары как тип по умолчанию A1, а 2-ой элемент – как A2 pair (); // конструктор инициализирует 1-ый элемент пары как v1, а 2-ой элемент - как v2 pair (const A1 &v1, const A2 &v2); // конструктор (шаблон) инициализирует 1-ый элемент пары как elem.fst, // а 2-ой - как elem.sec template <class b1, class b2> pair (const pair <b1, b2> &elem ); // конструктор инициализирует 1-ый элемент пары как v1, // а 2-ой элемент - как v2 template <class b1, class b2> pair (const pair <b1 v1, b2 v2> &elem ); } |
Реализация сделанных объявлений выглядит следующим образом.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
#include <utility> #include <map> #include <iomanip> #include <iostream> #include <conio.h> #include <windows.h> int main () { SetConsoleCP(1251); SetConsoleOutputCP(1251); setlocale(LC_ALL,"russian"); using namespace std; // используемое пространство имён pair <int, double> p1 (10, 0.011); // через конструктор инициализируем пару // p1, в которой 1-ый элемент имеет целый // тип, а 2-ой – действительный двойной // точности pair <int, double> p2; // здесь просто инициализируем пару p2 без // присвоения его двум полям конкретных // значений p2 = make_pair (10, 0.222); // какая-то ф-ция, обеспечивающая // присвоение значений полям пары p2 pair <int, double> p3 (p1); // инициализация пары p3, как копии уже // созданной p1 cout.precision (3); // назначаем точность вывода // действительных чисел // проверяем результаты работы выводом на устройство вывода по умолчанию cout << "Пара p1 состоит из элементов : ( " << p1.first << ", " << p1.second << " )" << endl; cout << "- p2 : ( " << p2.first << ", " << p2.second << " )" << endl; cout << "- p3 : ( " << p3.first << ", " << p3.second << " )" << endl; // пример более сложного применения pair, когда структура представляет собой // массив пар, оба элемента в которой целые числа (на примере карты памяти) map <int, int> mydb; // это наша база данных, как карта памяти map <int, int> :: iterator mydbindex; typedef pair <int, int> mypair; // определяет тип пары mypair, в которой // каждые 2 элемента – целые числа mydb.insert ( mypair (1, 10) ); // одновременно, через конструктор // создаётся пара, её элементам // присваиваются значения 1 и 10 и // она вставляется в базу // Внимание! Предполагается, что функция // вставки insert() реализована в базе map mydb.insert ( mypair (2, 20) ); mydb.insert ( mypair (3, 30) ); // после данной вставки в базе уже 3 пары // элементов cout << "База mydb состоит из следующих элементов : "; // просматриваем всю базу через её индекс, // получая 3 пары в формате «(число, число)» // Внимание! Предполагается, что функции begin() и end() в базе реализованы for ( mydbindex = mydb.begin(); mydbindex != mydb.end(); mydbindex++ ) cout << "(" << mydbindex -> first << ", " << mydbindex -> second << ")"; // Внимание! Обратите внимание на следующее объявление! // Создаются две пары res1 и res2, как результат вставки нового элемента в базу mydb // 1-ое поле каждой пары индексом на вставленную позицию в пару, а // 2-ое отвечает за результат вставки – успешна/неуспешна – тип логический pair <map <int, int> :: iterator, bool> res1, res2; res1 = mydb.insert ( mypair (4, 40) ); // помимо собственно вставки в базу // пары 4 и 40 insert() выдаёт true или false // в случае успеха или неуспеха операции res2 = mydb.insert ( mypair (1, 10) ); if (res1.second == true) { cout << "Пара (4, 40) успешно вставлена в базу" << endl; } else { cout << "Пара с ключевым первым элементом " << " ( (res1.fst) -> fst ) = " << ( res1.first ) -> first << " уже есть в базе, вставка невозможна" << endl; } if (res2.second == true) { cout << "Пара (1, 10) успешно вставлена в базу" << endl; } else { cout << "Пара с ключевым первым элементом " << " ( (res2.fst) -> fst ) = " << ( res2.first ) -> first << " уже есть в базе, вставка невозможна" << endl; } getch(); } |
Заметим, пара (4, 40) будет вставлена успешно, а (1, 10) – нет, пара (или запись, если переходить к терминологии баз данных) с ключом «1» уже в базе есть. Такое поведение один к одному соответствует поведению любой базы данных, когда она принимает данные с ключевыми полями. Собственно в этом и заключается особая ценность структуры pair и принципов работы с ней.