Привіт усім! В даній статті розглядається шаблонний клас std::set з бібліотеки C++ STL. std::set являється контейнером, який може зберігати унікальні елементи у певному порядку. Елементи в контейнері set не можуть бути модифікованими - вони завжди константні, але елементи можуть вставлятися і видалятися. Всередині контейнеру, елементи завжди розміщені у певному порядку, визначеним його спеціальним внутрішнім об'єктом, який призначений для порівняння елементів. Контейнер std::set зазвичай реалізований у якості бінарного дерева. Щоб використовувати даний клас, необхідно у файлі, де повинен бути присутній set, вказати наступний рядок:
#include <set> /* файл у якому міститься клас std::set */
Коментар вказувати не обов'язково.

Оголошення класу

template < class T,                        // set::key_type/value_type (тип елементів)
           class Compare = less<T>,        // set::key_compare/value_compare (компаратор елементів)
           class Alloc = allocator<T>      // set::allocator_type (об’єкт виділення)
           > class set ;

Методи

iterator set::begin () ;
Повертає ітератор на початок послідовності елементів контейнера.
iterator set::end () ;
Повертає ітератор на не існуючий елемент, який знаходиться за останнім елементом послідовності.
reverse_iterator set::rbegin () ;
Повертає ітератор зворотнього перебору, який вказує на кінець послідовності.
reverse_iterator set::rend () ;
Повертає ітератор зворотнього перебору, який вказує на не існуючий елемент, який знаходиться за першим лементом контейнера.
bool set::empty () const ;
Повертає true, якщо об'єкт класу std::set порожній.
size_type set::size () const ;
Повертає кількість елементів у контейнері.
size_type set::max_size () const ;
Повертає максимально можливий розмір контейнера.
pair<iterator,bool> set::insert (const value_type& val) ;
iterator set::insert (iterator position, const value_type& val) ;
template <class InputIterator> void set::insert (InputIterator first, InputIterator last) ;
Вставляє елементи в послідовність. Варіант з одним параметром, за відсутності елементу з значенням val, спричиняє збільшення послідовності на один елемент з значенням val. У варіантами з двома параметрами, перший параметр призначений для вказування приблизного місця вставки елементу з значенням val, якщо даний елемент ще не присутній у послідовності. Останній варіант призначений для вставки множини елементів у послідовність.
void set::erase (iterator position) ;
size_type set::erase (const value_type& val) ;
void set::erase (iterator first, iterator last) ;
Дані методи видаляють елементи з послідовності. Перший варіант видаляє елемент послідовності, на який вказує ітератор position.
void set::swap (set& x) ;
Даний метод викликає обмін внутрішніми даними між двома контейнерами класу std::set, після чого усі елементи об'єкту x переходять до поточного об'єкту, а елементи з поточного елементу переміщаються до об'єкта x. Після його виклику усі ітератори і вказівники залишаються валідними.
void set::clear () ;
Даний метод спричиняє видалення усіх елементів з поточного об'єкту.
key_compare set::key_comp () const ;
Повертає копію об'єкта порівняння, який використовує поточний контейнер для сортування елементів. Даний метод повертає об'єкт, або вказівник на функцію, який приймає два параметри - елементи які необхідно порівняти. Об'єкт за умовчанням поводить себе як оператор "<".
value_compare set::value_comp () const ;
Повертає копію об'єкта порівняння, який використовує поточний контейнер для сортування елементів.
iterator set::find (const value_type& val) const ;
Виконує пошук у елементах поточного об'єкта класу std::set і повертає ітератор на найдений елемент. Якщо вказаний елемент не присутній в послідовності повертається ітератор аналогічний тому, який повертає метод set::end().
size_type set::count (const value_type& val) const ;
Виконує образунок кількості значень в об'єкті. В зв'язку з тим, що елементи в примірнику даного класу унікальні, даний метод завжди повертає або 1, якщо елемент присутній в послідовності, або 0, якщо ні.
iterator set::lower_bound (const value_type& val) const ;
Повертає ітератор на елемент поточного об'єкту класу set, який повинен повинен бути еквівалентним або більшим за значення val, або ітератор аналогічний ітератору set::end().
iterator set::upper_bound (const value_type& val) const ;
Повертає ітератор на перший елемент, який повинен розміщуватись за елементом з значенням val.
pair<iterator,iterator> set::equal_range (const value_type& val) const ;
Повертає пару ітераторів, які обмежують проміжок, в який заключені елементи з еквівалентним до val значенням. Оскільки елементи в примірниках шаблону унікальні, метод повертає проміжок, який об'єднує максимум один елемент.
allocator_type set:: get_allocator () const ;
Повертає копію об'єкта, який використовується поточним об'єктом для створення примірників послідовності.

Приклади

Приклад #1

Розглянемо приклад використання основних методів контейнера set - додавання, видалення і перебір елементів.
#include <iostream> /* ввід/вивід (об'єкт cout) */
#include <set> /* файл у якому міститься клас std::set */
using namespace std ; /* друкуємо set без std */

/* головна функція програми */
int main (int argc, char** argv) 
{
  /* створюємо об'єкт множини, 
  ** який буде містити елементи типу вбудованого типу int */
  set<int> s1 ;
  
  /* вставляємо в множину елементи за допомогою методу insert
  ** об'єкт сам приймає рішення, як розмістити елементи по порядку
  ** відносно внутнішніх встановлених об'єктів порівняння */
  s1.insert (1) ;
  s1.insert (3) ;
  s1.insert (5) ;
  s1.insert (7) ;
  
  /* створюємо масив, значення якого перенесемо у об'єкт s1 */
  int iArray [] = {11, 13, 17, 19} ;
  
  /* вставляємо в множину декілька елементів,
  ** які містяться в масиві цілих знакових чисел */
  s1.insert (iArray, /* адреса початку масиву */
             iArray + sizeof(iArray)/sizeof(iArray[0])) ; /* розраховуємо адресу
                                                          ** кінцевого елементу */
  
  /* для наглядності */
  cout << "s1: " ;
  
  for (set<int>::iterator iter=s1.begin() ; /* створюємо ітератор на початок множини */
       iter!=s1.end() ; /* не виходимо за межі послідовності */
       ++iter) /* інкремент ітератора */
  {
    /* виводимо у термінал значення, 
    ** на яке вказує ітератор iter в даний момент,
    ** через синтаксичну конструкцію 
    ** на подобі розйменування звичайного вказівника */
    cout << *iter << "; " ; 
  }
  cout << endl ;
  
  /* видаляємо перший елемент з множини */
  s1.erase (s1.begin()) ; 
  
  /* видаляємо підмножину елементів */
  s1.erase (s1.find(5), s1.find(11)) ;
  
  /* видаляємо елемент з значенням 17 */
  s1.erase (17) ;
  
  cout << "s1: " ;
  /* виводимо результат видалень у термінал */
  for (set<int>::iterator iter=s1.begin() ; iter!=s1.end() ; ++iter)
  { cout << *iter << "; " ; }
  cout << endl ;
  
  return 0 ;
}
Даний код виведе на термінал наступні дані:

cpp_std_set_sample1-cxx_demo_program_for_using_set

Приклад #2

Розглянемо приклад, в якому Я найчастіше використовую клас set — це наділяння об'єктам створених мною класів певних характеристик. В даному випадку характеристика об'єкта зберігається у об'єктах класу string, тобто рядок символів.
#include <iostream> /* ввід/вивід (об'єкт cout) */
#include <string> /* рядковий тип */
#include <set> /* файл у якому міститься клас std::set */
using namespace std ; /* друкуємо set без std */

/* клас який призначений для 
** утримання параметрів товару */
class goods
{

 public :
 
  /* метод повертає посилання (reference)
  ** на об'єкт g_name, який віображає ім'я товару */
  string& name () ;
 
  /* методи для управління характеристиками товару */
  void add_property (string prop) ; /* додаємо нову властивість */
  bool has_property (string prop) ; /* перевіряємо присутність властивості */
  void del_property (string prop) ; /* видаляємо властивість з товару */
  
  /* методи делегати */
  set<string>::iterator begin () ;
  set<string>::iterator end () ;
  
  /* інформація у термінал */
  void print () ;
 
  /* конструктори для ініціалізації об'єкта */
  goods () ; /* звичайний конструктор без параметрів */
  goods (string name) ; /* конструктор встановлює ім'я */
  goods (string name, set<string> props) ; /* конструктор встановлює ім'я і параметри */
 ~goods () ; /* деструктор об'єкта */
 
 private:
 
  /* поля класу */
  string g_name ; /* містить ім'я товару */
  set<string> g_props ; /* містить властивості товару */
 
} ;

/* 
** реалізвуємо методи класу goods 
*/

string& goods :: name () 
{ return g_name ; }

void goods :: add_property (string prop)
{
  /* якщо назва властивості не пуста 
  ** додаємо її до поточних */
  if (prop.size()>0) 
  { g_props.insert (prop) ; }
}

bool goods :: has_property (string prop)
{
  /* виконуємо пошук у властивостях товару 
  ** за допомогою find,
  ** якщо елемент знайдено у послідовності (тобто find повернула
  ** ітератор, який не вказує на кінець послідовності) - 
  ** повертаємо значення true*/
  
  return ( g_props.find (prop) != g_props.end () ) ;
}

void goods :: del_property (string prop)
{ g_props.erase (prop) ; }

/* методи делегати */
set<string>::iterator goods :: begin ()
{ return g_props.begin () ; }

set<string>::iterator goods :: end ()
{ return g_props.end () ; }

void goods :: print ()
{
  /* виводимо основну інформацію на екран */
  cout << "Name: " << name() << endl << "Properties: " ;
  
  for (set<string>::iterator iter=begin();
       iter!=end();
       ++iter) 
  { cout << *iter << "; " ; }
  
  cout << endl ;
}

goods :: goods () 
{}

goods :: goods (string name) 
{ g_name = name ; }

goods :: goods (string name, set<string> props)
{
  g_name = name ;
  g_props.insert (props.begin(), props.end()) ;
}

goods :: ~goods () 
{}

/* головна функція програми */
int main (int argc, char** argv) 
{
  /* множина для характеристик за умовчанням
  ** для продуктів харчування */
  set<string> defprops ;
 
  /* додаємо ознаки до множини за умовчанням*/ 
  defprops.insert (string("fresh")) ; /* продукт свіжий */
  defprops.insert (string("delitious")) ; /* продукт смачний */
  
  /* створюємо два примірника класу goods - 
  ** перший з назвою "Potato 1" і інший "Tomato 1" 
  ** так як це продукти ініціалізуємо їхні властивості у 
  ** значення за умовчанням */
  goods potato (string("Potato 1"), defprops) ,
        tomato (string("Tomato 1"), defprops) ;
        
  /* додамо окремі унікальні характеритики до
  ** кожного об'єкта-продукта */
  potato.add_property (string("brown")) ; /* картоплина коричнева */
  tomato.add_property (string("berry")) ; /* томат ягода */
  
  /* видалимо властивість fresh з об'єкта potato */
  potato.del_property (string("fresh")) ;
  
  /* виводимо основну інформацію про наявні товари */
  potato.print () ;
  tomato.print () ;
  
  return 0 ;
}
Після запущення програми на виконання ми можемо отримати наступний результат: cpp_std_set_sample-cxx_demo_set_program_for_custom_class_and_setВ даному прикладі ми створили клас "goods", який призначений для зберігання ім'я товару і його характеристик. В якості контейнера для характеристик ми обрали клас set, тому що нам не важливі індекси характеристик (які є у масивах чи векторах), нам важлива сама характеристика товару. В якій послідовності вона буде розміщеною не важливо. Хоча в даному випадку set виконує деяку оптимізацію совоїм порядком - у сортованій послідовності пошук відбувається набато швидше ніж не у сортованій. В головній функції програми створено два примірника класу "goods": "potato" і "tomato", на яких ми і продемонстрували усю  потужність методів цього класу.

Резюме

Шаблонний клас std::set являється хорошим контейнером для збереження відсортованих унікальних елементів. Дозволяє швидко виконувати пошук в множині своїх елементів завдяки своїй внутрішній структурі і простий у використанні. Запобігає всю ручну тяганину з управлінням пам'яті.