77. Вместо массивов используйте vector
и string
Избегайте реализации абстракция массива посредством массивов в стиле С, арифметики указателей и примитивов управления памятью. Использование vector
или string
не только сделает проще вашу жизнь, но и позволит написать более безопасную и масштабируемую программу.
Сегодня едва ли не основными вопросами при написании программного обеспечения являются вопросы безопасности и переполнения буфера. Ограничения, связанные с фиксированным размером массивов, доставляют немало беспокойства, даже если приложению удается остаться в рамках корректности. Все эти проблемы связаны с использованием старых средств С, таких как встроенные массивы, указатели и их арифметика, а также управление памятью вручную вместо таких высокоуровневых концепций, как буферы, векторы и строки.
Вот некоторые из причин, по которым массивам в стиле С следует предпочесть стандартные средства С++.
• reallос
и соответствующих обновлений указателей.
•
• vector
и string::c_str
могут быть переданы функциям API на языке С. В частности, в С++ vector
служит "переходником" к С и другим языкам программирования (см. рекомендации 76 и 78).
•
• vector
и string
при выборе между эффективностью и безопасностью решение принимается в пользу эффективности (естественно, не в отладочном режиме). Тем не менее, стандартные средства оказываются гораздо лучшей платформой для создания безопасных компонентов, чем обычные массивы и указатели.
•
Применение массива может быть оправданно, когда его размер фиксирован на стадии компиляции (например, float[3]
для трехмерной точки; переход к четвертому измерению все равно потребует перепроектирования программы).
78. Используйте vector
(и string::c_str
) для обмена данными с API на других языках
vector
и string::c_str
служат шлюзом для сообщения с API на других языках. Однако не полагайтесь на то, что итераторы являются указателями; для получения адреса элемента, на который ссылается vector
, используйте выражение &*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
как обычный указатель T*
, итераторы могут быть (и все чаще так оно и есть) полноценными типами (еще раз см. рекомендацию 83).