• Они практически не снижают эффективность ваших программ. В классах vector
и string
при выборе между эффективностью и безопасностью решение принимается в пользу эффективности (естественно, не в отладочном режиме). Тем не менее, стандартные средства оказываются гораздо лучшей платформой для создания безопасных компонентов, чем обычные массивы и указатели.
• Они стимулируют оптимизацию. Современные реализации стандартной библиотеки включают оптимизации, о которых многие из нас просто никогда бы и не подумали.
Применение массива может быть оправданно, когда его размер фиксирован на стадии компиляции (например, float[3]
для трехмерной точки; переход к четвертому измерению все равно потребует перепроектирования программы).
Ссылки
[Alexandrescu01а] • [Dewhurst03] §13, §60, §68 • [Meyers01] §13, §16 • [Stroustrup00] §3.5-7, §5.3, §20.4.1, §C.7 • [Sutter00] §36
78. Используйте vector
(и string::c_str
) для обмена данными с API на других языках
Резюме
vector
и string::c_str
служат шлюзом для сообщения с API на других языках. Однако не полагайтесь на то, что итераторы являются указателями; для получения адреса элемента, на который ссылается vector::iterator iter
, используйте выражение &*iter
.
Обсуждение
vector
(в первую очередь) и string::c_str
и string::data
(во вторую) представляют собой наилучшие способы обмена данными с API на других языках вообще и с библиотеками на С в частности.
Данные vector
всегда хранятся последовательно, так что получение адреса первого элемента вектора дает указатель на его содержимое. Используйте &*v.begin()
, &v[0]
или &v.front()
для получения указателя на первый элемент v
. Для получения указателя на n-й элемент вектора лучше сначала провести арифметические вычисления, а затем получить адрес (например, &v.begin()[n]
или &v[n]
) вместо получения указателя на начало данных с последующим применением арифметики указателей (например, (&v.front())[n]
). Это связано с тем, что в первом случае в отладочной реализации выполняется проверка на доступ к элементу за пределами v
(см. рекомендацию 83).
Нельзя полагаться на то, что v.begin()
возвращает указатель на первый элемент или, в общем случае, что итераторы вектора являются указателями. Хотя некоторые реализации STL определяют vector::iterator
как обычный указатель T*
, итераторы могут быть (и все чаще так оно и есть) полноценными типами (еще раз см. рекомендацию 83).
Хотя в большинстве реализаций для string
также используется непрерывный блок памяти, это не гарантируется стандартом, так что никогда не используйте адрес символа в строке, считая его указателем на все содержимое строки. Хорошая новость заключается в том, что функция string::c_str
всегда возвращает строку в стиле С с завершающим нулевым символом ( string::data
также возвращает указатель на непрерывный блок памяти, но не гарантирует наличия завершающего нулевого символа).
Когда вы передаете указатель на данные объекта v
типа vector
, код на языке С может как читать, так и записывать элементы v
; однако он не должен выходить за границы данных. Хорошо продуманный API на языке С должен получать наряду с указателем либо максимальное количество объектов (до v.size()
), либо указатель на элемент, следующий за последним ( &*v.begin()+v.size()
).
Если у вас есть контейнер объектов типа T
, отличный от vector
или string
, и вы хотите передать его содержимое (или заполнить его) функции API на другом языке программирования, которая ожидает указатель на массив объектов типа T
, скопируйте содержимое контейнера в (или из) vector
, который может непосредственно сообщаться с такими функциями.
Ссылки
[Josuttis99] §6.2.3, §11.2.4 • [Meyers01] §16 • [Musser01] §B • [Stroustrup00] §16.3.1
79. Храните в контейнерах только значения или интеллектуальные указатели
Резюме
Храните к контейнерах объекты-значения. Контейнеры полагают, что их содержимое имеет тип значения, включая непосредственно хранящиеся значения, интеллектуальные указатели и итераторы.
Обсуждение
Наиболее распространенное использование контейнеров — для непосредственного хранения значений (например, vector, set
). В случае контейнеров указателей, если контейнер владеет объектами, на которые указывает, то лучше использовать контейнер интеллектуальных указателей со счетчиком ссылок (например, list >
); в противном случае можно выбрать контейнер обычных указателей (например, list
) или иных значений, подобных указателям — таких как итераторы (например, list::iterator>
).
Читать дальше