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).