Строка С++ недостаточно оптимизирована для строковых литералов
В моем проекте на С++ я за один шаг до замены всего char*
на std::string
, но я нахожу один случай, когда std::string
терпит неудачу.
Представьте, что у меня есть две функции:
void foo1(const std::string& s)
{
...
}
void foo2(const char* s)
{
...
}
Если я напишу что-то вроде этого:
const char* SL = "Hello to all!";
foo1(SL); // calls malloc, memcpy, free
foo2(SL);
в foo1
SL
будет неявно преобразован в std::string
. Это означает, что конструктор std::string
будет выделять память, и он скопирует строковый литерал в этот буфер. В foo2
, хотя ничего из этого не произойдет.
В большинстве реализаций std::string
предполагается супер оптимизироваться (например, копировать по записи), но когда я построю его с помощью const char*
, это не так. И мой вопрос в следующем: почему это происходит? Я что-то упускаю? Является ли моя стандартная библиотека недостаточно оптимизированной или по какой-либо причине (что я не знаю) это абсолютно небезопасно?
Ответы
Ответ 1
На самом деле, ваши заботы исчезнут (*), если вы изменили литерал:
std::string const SL = "Hello to all!";
Я добавил const
для вас.
Теперь вызов foo1
не будет содержать никакого копирования (вообще), а вызов foo2
может быть достигнут за небольшую плату:
foo1(SL); // by const-reference, exact same cost than a pointer
foo2(SL.c_str()); // simple pointer
Если вы хотите перейти на std::string
, не только переключайте интерфейсы функций, также переключайте переменные (и константы).
(*) Исходный ответ предполагал, что SL
является глобальной константой, если она является переменной, локальной для функции, тогда ее можно было бы сделать static
, если вы действительно хотите избежать ее построения при каждом вызове.
Ответ 2
Проблема заключается в том, что класс std::string не может распознать, является ли указатель const char*
глобальным символьным литералом или нет:
const char *a = "Hello World";
const char *b = new char[20];
Указатель char * может быть недействителен в любое время (например, когда он локальная переменная и функция/область заканчивается), таким образом std::string
должен стать эксклюзивным владельцем строки. Это может быть достигнуто только при копировании.
В следующем примере показано, почему это необходимо:
std::string getHelloWorld() {
char *hello = new char[64];
strcpy(hello, "Hello World");
std::string result = (const char *)hello; // If std::string didn't make a copy, the result could be a garbage
delete[] hello;
return result;
}
Ответ 3
std::string
не является серебряной пулей. Он предназначен для наилучшей реализации универсальной изменяемой строки, которая владеет своей памятью и которая достаточно дешева для использования с C API. Это обычные сценарии, но они не соответствуют каждому экземпляру использования строки.
Строковые литералы, как вы говорите, не подходят для этого варианта использования. Они используют статически выделенную память, поэтому std::string
не может и не должен пытаться завладеть памятью. И эти строки всегда доступны только для чтения, поэтому std::string
не позволяет вам изменять их.
std::string
создает копию переданных ему строковых данных, а затем внутренне работает с этой копией.
Если вы хотите работать с постоянными строками, чье время жизни обрабатывается в другом месте (в случае строковых литералов оно обрабатывается библиотекой времени выполнения, которая инициализирует и освобождает статические данные), тогда вы можете использовать другое строковое представление. Возможно, просто простой const char*
.