char * vs std :: string в c ++ [закрыто]

81

Когда я должен использовать std::stringи когда я должен использовать char*для управления массивами chars в C ++?

Похоже, вам следует использовать, char*если производительность (скорость) имеет решающее значение, и вы готовы принять некоторые из рискованных дел из-за управления памятью.

Можно ли рассмотреть другие сценарии?

jww
источник

Ответы:

56

Вы можете передать std :: strings по ссылке, если они большие, чтобы избежать копирования, или указатель на экземпляр, поэтому я не вижу никаких реальных преимуществ использования указателей char.

Я использую std :: string / wstring для более или менее всего, что является фактическим текстом. char *однако полезно для других типов данных, и вы можете быть уверены, что они будут освобождены, как и должны. В противном случае std :: vector - это путь.

Из всего этого, наверное, есть исключения.

Скурмедель
источник
8
есть ли разница в производительности перед двумя?
vtd-xml-author
3
@ vtd-xml-author: Может быть. У стрита char *почти нет накладных расходов. std::stringЯ не знаю, какие именно накладные расходы , скорее всего, это зависит от реализации. Я вряд ли ожидаю, что накладные расходы будут намного больше, чем у простого указателя char. Поскольку у меня нет копии стандарта, я не могу подробно описать какие-либо гарантии, предоставляемые стандартом. Любая разница в производительности, вероятно, будет зависеть от выполняемых операций. std::string::sizeможет хранить размер рядом с символьными данными и, таким образом, быть быстрее, чем strlen.
Skurmedel
2
Почему бы не использовать std :: string для нетекстовых данных? Они не завершаются нулем, поэтому вы можете хранить там все, что захотите.
Кейси Родармор
1
@rodarmor Вы можете хранить все, что хотите, хотя это сопряжено с риском, поскольку строка предназначена для символьных строк с завершающим нулем. Вы должны быть осторожны, чтобы использовать только безопасные для двоичного кода операции, например, append(const string&)и append(const char*, size_t)вместо operator+=().
boycy
6
Вы уверены? Я знаю, что многие операции предполагают, что char * является строкой с завершающим нулем, но я не могу придумать ничего, что предполагало бы, что std :: string не содержит нулей.
Кейси Родармор,
58

Моя точка зрения такова:

  • Никогда не используйте char *, если вы не вызываете код "C".
  • Всегда используйте std :: string: это проще, удобнее, оптимизировано, стандартно, избавит от ошибок, проверено и доказано, что работает.
Гал Гольдман
источник
13

Использование необработанной строки

Да, иногда это действительно удается. При использовании массивов const char *, char, выделенных в стеке, и строковых литералов вы можете сделать это таким образом, чтобы память не выделялась вообще.

Написание такого кода часто требует большего мышления и осторожности, чем использование строки или вектора, но с помощью надлежащих методов это можно сделать. При правильной технике код может быть безопасным, но вам всегда нужно быть уверенным, что при копировании в char [] у вас либо есть гарантии относительно длины копируемой строки, либо вы проверяете и обрабатываете слишком большие строки изящно. Невыполнение этого требования и дало семейству функций strcpy репутацию небезопасных.

Как шаблоны могут помочь в написании безопасных буферов символов

Что касается безопасности буферов char [], могут помочь шаблоны, поскольку они могут создавать инкапсуляцию для обработки размера буфера за вас. Подобные шаблоны реализованы, например, Microsoft, чтобы обеспечить безопасную замену strcpy. Пример здесь извлечен из моего собственного кода, в реальном коде гораздо больше методов, но этого должно быть достаточно, чтобы передать основную идею:

template <int Size>
class BString
{
  char _data[Size];

  public:
  BString()
  {
    _data[0]=0;
    // note: last character will always stay zero
    // if not, overflow occurred
    // all constructors should contain last element initialization
    // so that it can be verified during destruction
    _data[Size-1]=0;
  }
  const BString &operator = (const char *src)
  {
    strncpy(_data,src,Size-1);
    return *this;
  }

  operator const char *() const {return _data;}
};

//! overloads that make conversion of C code easier 
template <int Size>
inline const BString<Size> & strcpy(BString<Size> &dst, const char *src)
{
  return dst = src;
}
Suma
источник
1
+1 за «При использовании const char *, массивов char, выделенных в стеке, и строковых литералов, вы можете сделать это таким образом, чтобы память не выделялась вообще». Люди забывают, что «выделение» стека происходит намного быстрее, чем кучи.
NoSenseEtAl
char*строки не всегда в стеке. char *str = (char*)malloc(1024); str[1024] = 0;
Коул Джонсон,
@ColeJohnson Я не утверждаю этого, я просто имею в виду, что если вы хотите, чтобы ваша строка выделялась в стеке, вам нужно использовать const char * в сочетании со строковыми литералами, а не std :: string.
Suma
9

Один случай , который вы должны использовать , char*а не std::stringкогда вам нужны статические строковые константы. Причина в том, что у вас нет никакого контроля над модулями заказа, инициализирующими свои статические переменные, а другой глобальный объект из другого модуля может ссылаться на вашу строку до ее инициализации. http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Static_and_Global_Variables

std::string плюсы:

  • управляет памятью за вас (строка может расти, и реализация выделит вам больший буфер)
  • Интерфейс программирования более высокого уровня, прекрасно работает с остальной частью STL.

std::stringМинусы: - два разных экземпляра строки STL не могут использовать один и тот же базовый буфер. Поэтому, если вы переходите по значению, вы всегда получаете новую копию. - есть некоторое снижение производительности, но я бы сказал, что если ваши требования не являются специальными, это незначительно.

Самет
источник
Фактически, реализации STL часто реализуют семантику копирования при записи для std :: string, поэтому передача их по значению совсем не стоит больших затрат. Тем не менее, на это лучше не полагаться и в любом случае лучше передать ссылку на const.
1
Некоторые реализации std :: string отказались от реализации COW. Более того, это не так тривиально, как кажется, что он обеспечивает потокобезопасный класс (POSIX), совместимый со стандартом. См. Groups.google.fr/group/ifi.test.boa/browse_frm/thread/… или groups.google.fr/group/comp.programming.threads/browse_frm/…
Люк Эрмитт,
8

Вам следует подумать об использовании char*в следующих случаях:

  • Этот массив будет передан в параметре.
  • Вы заранее знаете максимальный размер вашего массива (вы его знаете ИЛИ навязываете).
  • Вы не будете выполнять никаких преобразований в этом массиве.

На самом деле, в C ++ char*часто используются фиксированные маленькие слова, такие как параметры, имя файла и т. Д.

Жером
источник
3
Массив не передается, указатель на массив. Вот что такое указатель - указатель на объект.
Коул Джонсон,
5

Когда использовать c ++ std :: string:

  • Строки в целом более безопасны, чем char *. Обычно, когда вы делаете что-то с char *, вам нужно все проверять, чтобы убедиться, что все в порядке, в классе строк все это делается за вас.
  • Обычно при использовании char * вам нужно освободить выделенную память, вам не нужно делать это со строкой, поскольку она освободит свой внутренний буфер при разрушении.
  • Строки хорошо работают со строковым потоком C ++, форматировать ввод-вывод очень просто.

Когда использовать char *

  • Использование char * дает вам больше контроля над тем, что происходит «за кулисами», что означает, что вы можете настроить производительность, если вам нужно.
user88637
источник
3

Используйте (const) char * в качестве параметров, если вы пишете библиотеку. Реализации std :: string различаются между разными компиляторами.

Неманья Трифунович
источник
Если вы пишете библиотеку на C ++, макет std :: string - не единственное, о чем вам нужно беспокоиться. Между двумя реализациями существует множество потенциальных несовместимостей. Используйте библиотеки в C ++, только если они доступны в исходном коде или скомпилированы для конкретного компилятора, который вы используете. Библиотеки C обычно более переносимы, но в этом случае у вас все равно нет std :: string.
Дэвид Торнли
Верно, что std :: string - не единственная проблема, но это слишком много, чтобы делать вывод «Используйте библиотеки в C ++, только если они доступны в исходном коде или скомпилированы для конкретного компилятора, который вы используете». Существуют компонентные системы, которые отлично работают с различными компиляторами (например, COM), и можно предоставить интерфейс C библиотеке, которая внутренне написана с помощью C ++ (например, Win32 API)
Неманья Трифунович
2

Если вы хотите использовать библиотеки C, вам придется иметь дело с C-строками. То же самое применимо, если вы хотите предоставить свой API для C.

n0rd
источник
2

Вы можете ожидать, что большинство операций с std :: string (например, например find) будут максимально оптимизированы, поэтому они, вероятно, будут работать как минимум так же хорошо, как и чистый аналог C.

Также стоит отметить, что итераторы std :: string довольно часто сопоставляются с указателями в базовом массиве char. Таким образом, любой алгоритм, который вы разрабатываете поверх итераторов, по сути идентичен тому же алгоритму поверх char * с точки зрения производительности.

На что следует обратить внимание, например, operator[]большинство реализаций STL не выполняют проверку границ и должны преобразовывать это в ту же операцию с базовым массивом символов. AFAIK STLPort может дополнительно выполнять проверку границ, после чего этот оператор будет немного медленнее.

Так что же дает вам использование std :: string? Это освобождает вас от ручного управления памятью; изменить размер массива становится проще, и обычно вам нужно меньше думать об освобождении памяти.

Если вы беспокоитесь о производительности при изменении размера строки, есть reserve функция, которая может вам пригодиться.


источник
1

если вы используете массив символов в подобном тексте и т.д., используйте std :: string более гибким и простым в использовании. Если вы используете его для чего-то еще, например, для хранения данных? использовать массивы (предпочитать векторы)

RvdK
источник
1

Даже когда производительность имеет решающее значение, лучше использовать vector<char>- он позволяет заранее выделить память (метод резерв ()) и поможет избежать утечек памяти. Использование vector :: operator [] приводит к накладным расходам, но вы всегда можете извлечь адрес буфера и проиндексировать его точно так же, как если бы это был char *.

острый зуб
источник
Но было бы неплохо использовать какие-то типичные строковые функции и иметь только возможность указать политику для хранилища. Для этого см. Ссылку в моем ответе.
Anonymous
На самом деле это не так. Если вы считаете, что вектор будет размещен в непрерывном пространстве памяти, перераспределение (для увеличения размера вектора) будет вообще неэффективным, поскольку подразумевает копию предыдущего фрагмента.
Jérôme
Я неправильно понял ваш ответ, поскольку вы используете вектор вместо char *, а не вместо строки ... В этом случае я согласен.
Jérôme
При использовании operator [] не должно быть накладных расходов. См., Например, stackoverflow.com/questions/381621/…
Люк Эрмитт
-1

AFAIK внутренне большинство std :: string реализует копирование при записи, семантику подсчета ссылок, чтобы избежать накладных расходов, даже если строки не передаются по ссылке.

Пётр
источник
5
Это больше не так, потому что копирование при записи вызывает серьезные проблемы масштабируемости в многопоточной среде.
Suma
По крайней мере, это верно для реализации STL в GCC.