Читать «Стандарты программирования на С++. 101 правило и рекомендация» онлайн - страница 125
Андрей Александреску
Данные vector
всегда хранятся последовательно, так что получение адреса первого элемента вектора дает указатель на его содержимое. Используйте &*v.begin()
, &v[0]
или &v.front()
для получения указателя на первый элемент v
. Для получения указателя на n-й элемент вектора лучше сначала провести арифметические вычисления, а затем получить адрес (например, &v.begin()[n]
или &v[n]
) вместо получения указателя на начало данных с последующим применением арифметики указателей (например, (&v.front())[n]
). Это связано с тем, что в первом случае в отладочной реализации выполняется проверка на доступ к элементу за пределами v
(см. рекомендацию 83).
Нельзя полагаться на то, что v.begin()
возвращает указатель на первый элемент или, в общем случае, что итераторы вектора являются указателями. Хотя некоторые реализации STL определяют vector<T>::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<T>
, который может непосредственно сообщаться с такими функциями.
Ссылки
79. Храните в контейнерах только значения или интеллектуальные указатели
Резюме
Храните к контейнерах объекты-значения. Контейнеры полагают, что их содержимое имеет тип значения, включая непосредственно хранящиеся значения, интеллектуальные указатели и итераторы.
Обсуждение
Наиболее распространенное использование контейнеров — для непосредственного хранения значений (например, vector<int>, set<string>
). В случае контейнеров указателей, если контейнер владеет объектами, на которые указывает, то лучше использовать контейнер интеллектуальных указателей со счетчиком ссылок (например, list<shared_ptr<Widget> >
); в противном случае можно выбрать контейнер обычных указателей (например, list<Widget*>
) или иных значений, подобных указателям — таких как итераторы (например, list<vector<Widget>::iterator>
).