Должен ли я возвращать std:: строки?

Я стараюсь использовать std::string вместо char*, когда это возможно, но я волнуюсь, что я могу ухудшить производительность слишком много. Является ли это хорошим способом возврата строк (без ошибок для краткости)?

std::string linux_settings_provider::get_home_folder() {
    return std::string(getenv("HOME"));
}

Кроме того, связанный с этим вопрос: при принятии строк в качестве параметров я должен получать их как const std::string& или const char*?

Спасибо.

Ответ 1

Верните строку.

Я думаю, что лучшая абстракция того стоит. Пока вы не сможете измерить значимую разницу в производительности, я бы сказал, что это микро-оптимизация, которая существует только в вашем воображении.

Прошло много лет, чтобы получить хорошую абстракцию строки в С++. Я не верю, что Бьярн Строуструп, столь известный своей консервативной "единственной оплатой за то, что вы используете" изречение, допустил бы очевидный убийца производительности на этом языке. Более высокая абстракция хороша.

Ответ 2

Верните строку, как говорят все.

при принятии строк в качестве параметров, должен ли я принимать их как const std::string& или const char*?

Я бы сказал, беру любые константные параметры по ссылке, если только они не достаточно легки для принятия по значению или в тех редких случаях, когда вам нужен нулевой указатель, чтобы быть допустимым значением, означающим "ни один из вышеперечисленных". Эта политика не относится к строкам.

Неконстантные ссылочные параметры являются спорными, потому что из вызывающего кода (без хорошей IDE) вы не можете сразу увидеть, переданы ли они по значению или по ссылке, и разница важна. Таким образом, код может быть неясным. Для параметров const это не применяется. Люди, читающие вызывающий код, обычно могут просто предположить, что это не их проблема, поэтому им иногда нужно проверять подпись.

В случае, когда вы собираетесь взять копию аргумента в функции, ваша общая политика должна состоять в том, чтобы принять аргумент по значению. Затем у вас уже есть копия, которую вы можете использовать, и если бы вы скопировали ее в какое-то определенное место (например, элемент данных), вы можете переместить ее (на С++ 11) или обменять ее (в С++ 03) на получите его там. Это дает компилятору наилучшую возможность оптимизировать случаи, когда вызывающий абонент передает временный объект.

В частности, для string это относится к случаю, когда ваша функция принимает значение std::string по значению, а вызывающий объект указывает в качестве выражения выражения строковый литерал или char*, указывающий на строку с нулевым завершением. Если вы взяли const std::string& и скопировали его в функцию, это приведет к построению двух строк.

Ответ 3

Стоимость копирования строк по значению зависит от реализации STL, с которой вы работаете:

  • std::string в MSVC используется оптимизация коротких строк, так что короткие строки (< 16 символов iirc) не требуют выделения памяти (они хранятся в самом std::string), а дольше каждый требует копирования кучи каждый раз при копировании строки.

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

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

re: передача параметров, имейте в виду, что существует стоимость перехода от char * → std::string, но не от перехода от std::string → char *. В общем, это означает, что вам лучше принять константную ссылку на std::string. Однако наилучшее оправдание принятия const std::string & как аргумент состоит в том, что тогда у вызывающего не должен быть дополнительный код для проверки против нуля.

Ответ 5

Человеческая натура беспокоится о производительности, особенно когда язык программирования поддерживает низкоуровневую оптимизацию. Однако мы не должны забывать, что программисты - это то, что производительность программы - это всего лишь одна вещь среди многих, которую мы можем оптимизировать и восхищаться. В дополнение к скорости программы мы можем найти красоту в нашей собственной работе. Мы можем свести к минимуму наши усилия, пытаясь достичь максимального визуального результата и интерактивности интерактивного интерфейса. Считаете ли вы, что это может быть больше мотивации, которая беспокоит биты и циклы в долгосрочной перспективе... Итак, да, верните строку: s. Они минимизируют размер вашего кода и ваши усилия, а также уменьшают количество работы, которую вы вкладываете.

Ответ 6

В вашем случае Оптимизация возвращаемого значения будет иметь место, поэтому std::string не будет скопирована.

Ответ 7

Остерегайтесь, когда вы пересекаете границы модулей.

Затем лучше всего возвращать примитивные типы, поскольку типы С++ не обязательно бинарны, совместимые в разных версиях одного и того же компилятора.

Ответ 8

Я согласен с другими плакатами, что вы должны использовать строку.

Но знайте, что в зависимости от того, насколько агрессивно ваш компилятор оптимизирует временные ряды, вы, вероятно, будете иметь дополнительные накладные расходы (используя динамический массив символов). (Примечание. Хорошей новостью является то, что в С++ 0a разумное использование ссылок rvalue не потребует оптимизации компилятора, чтобы покупать эффективность здесь - и программисты смогут сделать некоторые дополнительные гарантии производительности относительно своего кода, не полагаясь на качество компилятор.)

В вашей ситуации лишние накладные расходы требуют ручного управления памятью? Большинство разумных программистов не согласятся - но если ваше приложение действительно имеет проблемы с производительностью, следующим шагом будет профилирование вашего приложения - таким образом, если вы вводите сложность, вы делаете это только тогда, когда у вас есть хорошие доказательства того, что необходимо улучшить общая эффективность.

Кто-то сказал, что оптимизация возвращаемого значения (RVO) здесь не имеет значения - я не согласен.

В стандартном тексте (С++ 03) это читается (12.2):

[Начать стандартную цитату]

Временные классы типа класса создаются в различных контекстах: привязка rvalue к ссылке (8.5.3), возвращающая rvalue (6.6.3), преобразование, которое создает rvalue (4.1, 5.2.9, 5.2.11), 5.4), выбрасывая исключение (15.1), вводя обработчик (15.3) и в некоторые инициализации (8.5). [Примечание: время жизни объектов исключений описано в 15.1. ] Даже когда избегается создание временного объекта (12.8), все семантические ограничения должны соблюдаться, как если бы был создан временный объект. [Пример: даже если конструктор копирования не вызывается, все семантические ограничения, такие как доступность (пункт 11), должны быть выполнены. ]

 [Example:  
struct X {
  X(int);
  X(const X&);
  ˜X();
};

X f(X);

void g()
{
  X a(1);
  X b = f(X(2));
  a = f(a);
}

Здесь реализация может использовать временную конструкцию X (2), прежде чем передавать ее в f() с помощью Xs copy-constructor; альтернативно, X (2) может быть построено в пространстве, используемом для хранения аргумента. Кроме того, временное может использоваться для хранения результата f (X (2)) перед копированием его в b с помощью Xs copyconstructor; альтернативно, результат f() s может быть построен в b. С другой стороны, выражение a = f (a) требует временного либо для аргумента a, либо для результата f (a), чтобы избежать нежелательного сглаживания а. ]

[Конец стандартной цитаты]

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

В вашем коде нет ничего, что по своей сути запрещает RVO, но прочитайте вашу компиляторную документацию, чтобы убедиться, что вы действительно можете положиться на нее, если она вам действительно нужна.

Ответ 9

Я согласен с duffymo. Сначала вы должны сделать понятное рабочее приложение, а затем, если есть необходимость, оптимизировать атаку. Именно в этот момент у вас будет идея, где основные узкие места и будут иметь возможность более эффективно управлять временем при создании более быстрого приложения.

Ответ 10

Я согласен с @duffymo. Не оптимизируйте, пока вы не измерили, это имеет двойное значение при выполнении микро-оптимизации. И всегда: измерьте до и после, который вы оптимизировали, чтобы увидеть, действительно ли вы изменили ситуацию к лучшему.

Ответ 11

Верните строку, это не так большая потеря производительности, но она, несомненно, облегчит вам работу.

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

Ответ 12

Если вы передаете ссылочную строку и работаете над этой строкой, вам ничего не нужно возвращать.;)