На этом шаге мы рассмотрим особенности представления и использования класса 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(), производящая логическую инверсию, может применяться как ко всем битам, так и к отдельному биту вектора.
Операция | Описание |
---|---|
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.
Со следующего шага мы начнем рассматривать деки.