Шаг 140.
Библиотека STL.
Контейнеры STL. Класс vector<bool>

    На этом шаге мы рассмотрим особенности представления и использования класса vector<bool>.

    Для векторов, содержащих элементы логического типа, в стандартной библиотеке C++ определена специализированная разновидность класса vector. Это было сделано для того, чтобы оптимизированная версия занимала меньше места, чем стандартная реализация vector для типа bool. В стандартной реализации для каждого элемента резервируется минимум 1 байт. Во внутреннем представлении специализированной реализации vector<bool> каждый элемент обычно представляется одним битом, поэтому она занимает в восемь раз меньше памяти. Впрочем, оптимизация не дается даром: в C++ минимальное адресуемое значение должно иметь размер минимум 1 байт. Следовательно, специализированная версия требует специальной обработки ссылок и итераторов.

    В результате vector<bool> не удовлетворяет всем требованиям других векторов (в частности, значение vector<bool>preference не является полноценным l-значением, а итератор vector<bool>::iterator не является итератором произвольного доступа). Следовательно, код шаблона работает с векторами любого типа, за исключением bool. Вдобавок класс vector<bool> может уступать обычным реализациям по скорости работы, потому что операции с элементами приходится преобразовывать в операции с битами. Впрочем, внутреннее устройство vector<bool> зависит от реализации, поэтому эффективность (как скорость работы, так и затраты памяти) может быть разной.

    Учтите, что класс vector<bool> представляет собой нечто большее, чем специализацию класса vector<> для bool. В него также включена поддержка специальных операций, упрощающих работу с флагами и наборами битов.

    Размер контейнера vector<bool> изменяется динамически, поэтому его можно рассматривать как битовое поле с динамическим размером. Иначе говоря, вы можете добавлять и удалять биты. Если вы работаете с битовым полем, имеющим статический размер, вместо класса vector<bool> лучше использовать bitset.

    Дополнительные операции контейнера vector<bool> перечислены в таблице 1. Операция flip(), производящая логическую инверсию, может применяться как ко всем битам, так и к отдельному биту вектора.

Таблица 1. Специальные операции vector<bool>
Операция Описание
c.flip() Инвертирует все логические элементы
m[idx].flip() Инвертирует логический элемент с заданным индексом
m[idx] = val Присваивает значение логическому элементу с индексом idx (присваивание одного бита)
m[idxl] = m[idx2] Присваивает значение элемента с индексом idx2 элементу с индексом idxl

    Обратите внимание на возможность вызова flip() для одного логического элемента. На первый взгляд это выглядит странно, потому что оператор индексирования возвращает значение типа bool, а вызов flip() для этого базового типа невозможен. В данном случае класс vector<bool> использует стандартную методику, основанную на применении так называемых заместителей: для vector<bool> возвращаемое значение оператора индексирования (и других операторов, возвращающих элементы) оформлено в виде вспомогательного класса. Если вы хотите, чтобы возвращаемое значение интерпретировалось как значение типа bool, используется автоматическое преобразование типа. Для других операций определяются функции класса. Соответствующая часть объявления vector<bool> выглядит так:

namespace std {
  class vector<bool> { public:
    // Вспомогательный тип для оператора индексирования 
    class reference {
      .   .   .   .   .
      public:
        // Автоматическое преобразование типа к bool 
        operator bool() const;

        // Присваивание
        reference& operator= (const bool);
        reference& operator= (const reference&);

        // Инверсия бита 
        void flip();
    }
    .    .    .    .
    // Операции обращения к элементам
    // - возвращается тип reference вместо bool
    reference operator[] (size_type n);
    reference at(size_type n);
    reference front();
    reference back();
    .    .    .    .
  }    
}

    Как видно из этого фрагмента, все функции обращения к элементам возвращают тип reference. Следовательно, вы также можете использовать следующие команды:

  c.front().flip();   // Инверсия первого логического элемента
  c.at(5) = c.back(); // Присвоить последний элемент элементу с индексом 5

    Как обычно, вызывающая сторона должна проследить за тем, чтобы в векторе существовали первый, последний и шестой элементы.

    Внутренний тип reference используется только для неконстантных контейнеров типа vector<bool>. Константные функции обращения к элементам возвращают обычные значения типа bool.

    Со следующего шага мы начнем рассматривать деки.




Предыдущий шаг Содержание Следующий шаг