строка недостаточно оптимизирована для строковых литералов - PullRequest
13 голосов
/ 05 декабря 2011

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

Представьте, что у меня есть эти 2 функции:

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 предполагается супероптимизированным (например, Copy On Write), но когда я создаю его с const char*, это не так. И мой вопрос таков: почему это происходит? Я что-то пропустил? Моя стандартная библиотека недостаточно оптимизирована или по какой-то причине (о которой я не знаю) это совершенно небезопасно?

Ответы [ 3 ]

21 голосов
/ 05 декабря 2011

На самом деле ваши заботы исчезли бы (*), если бы вы изменили литерал:

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, еслиискренне желает не строить его при каждом вызове.

10 голосов
/ 05 декабря 2011

Проблема в том, что у класса 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;
}
5 голосов
/ 05 декабря 2011

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

Строковые литералы, как вы упоминаете, плохо подходят для этого варианта использования.Они используют статически распределенную память, поэтому std::string не может и не должна пытаться завладеть памятью.И эти строки всегда только для чтения, поэтому std::string не может позволить вам изменять их.

std::string создает копию данных строки, переданных ему, а затемработает с этой копией изнутри.

Если вы хотите работать с константными строками, время жизни которых обрабатывается в другом месте (в случае строковых литералов это обрабатывается библиотекой времени выполнения, которая инициализирует и освобождает статические данные), тогда вы можетехочу использовать другое строковое представление.Возможно, просто const char*.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...