exponenta event banner

Дорогая константа std::string строительство

Объект постоянной строки создается из постоянных данных, что приводит к неэффективному коду

Описание

Этот дефект возникает, когда оба этих условия верны:

  • Вы создаете std::string объект из постоянных данных, таких как строковый литерал или выходные данные constexpr функция.

  • std::string после построения объект остается постоянным или неизмененным.

Эта проверка не помечает переменные-члены класса и строковые литералы, которые являются аргументами функции.

Риск

Рассмотрим std::string объекты в кодовом блоке, содержащем постоянные данные, которые остаются неизмененными после построения. Каждый раз при выполнении блока кода создается новый std::string объект конструируется без изменения его содержимого. Повторное построение такого std::string объект без модификации контента неэффективен и его трудно обнаружить. Рассмотрим этот код:

#include <string>
constexpr char* getStrPtr() { 
	return "abcd"; 
}
void foo(){
	std::string s1 = "abcd";
	std::string s2{"abcd"};
	std::string s3 = getStrPtr();                 
}
int main(){
//...
for(int i = 0; i<10000; ++i)
      foo();
}
В этом коде функция foo называется 10000 времена. Каждый раз foo называется, s1, s2, и s3 создаются из одного и того же строкового литерала константы abcd, что приводит к неэффективному коду. Поскольку такой неэффективный и запутанный код правильно компилирует и функционирует, неэффективное построение std::string объекты из постоянных данных могут быть не замечены.

Зафиксировать

Исправление этого дефекта зависит от предполагаемого использования постоянных данных.

  • Постоянные данные можно сохранить в static строковый объект, если необходимы функциональные возможности std::string класс.

  • Постоянные данные можно использовать непосредственно в качестве временных литералов, если не требуется повторно использовать данные.

  • Постоянные данные можно сохранить с помощью const символьный массив или std::string_view если вам не нужны функциональные возможности std::string класс. std::string_view поддерживается C++ 17 и более поздними версиями.

Рассмотрим этот код:

constexpr char* getStrPtr() { 
	return "abcd"; 
}
void foo(){
	static  std::string s3 = getStrPtr();
	 std::string_view s3a{s3};                
}
int main(){
//...
for(int i = 0; i<10000; ++i)
      foo();
}
std::string объект s3 объявлен как static. Поскольку s3 является static, он конструируется только один раз, даже если foo называется 10000 времена. std::string_view объект s2 показывает содержимое s3 и избегает построения std::string объект каждый раз foo вызывается. С помощью std::string_view и static объекты, вы избегаете ненужного построения константы std::string объекты. Этот метод также поясняет, что объекты s3 и s3a представляют одни и те же данные.

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

Примеры

развернуть все

#include <string>
constexpr char* getStrPtr() { 
	return "abcd"; 
}
constexpr size_t FOUR(){
	return 4; 
}
size_t getCount();
void CallFunc(std::string s);
void foo(){
	std::string s1 = "abcd";
	std::string s2{"abcd"};
	std::string s3 = getStrPtr();  
	std::string s4("abcd", FOUR());
	std::string s5("abcd"), s6("abcd");
}

void bar(){
	std::string s3a("abcd", getCount());
	char *p = "abcd";
	std::string s_p = p;
	CallFunc("message");
}

В этом примере несколько const std::string объявляются объекты.

  • Polyspace ® помечает std::string объекты s1,s2, s5, и s6 потому что если эти строки создаются из постоянных данных каждый раз foo называется, но остается неизмененным после построения.

  • Флаги Polyspaces4 и s3 потому что они создаются из констант времени компиляции, таких как литерал константы abcd и выходные данные constexpr функция. Объект s3a не помечен, так как вывод getCount не является константой времени компиляции.

  • Polyspace не помечает эти объекты при их построении из постоянных данных:

    • Объект, который не является std::string, такие как *p.

    • Временные объекты, созданные как аргумент функции, например объект, содержащий строковый литерал message в аргументе CallFunc.

Исправление

Этот дефект можно исправить несколькими способами. Например:

  • Вы можете объявить std::string объекты как static. Когда объект staticкомпилятор не реконструирует его в разных областях. Когда вам нужны функциональные возможности std::string класс, это объявление является хорошим исправлением.

  • Константные данные можно хранить в символьном массиве или символьном указателе. Эти объекты менее дороги по сравнению с std::string.

  • Можно объявить постоянные строки как std::string_view объекты. Эти объекты не содержат копии постоянных строк, что делает эти объекты эффективными.

#include <string>
#include <string_view>
constexpr char* getStrPtr() { 
	return "abcd"; 
}
constexpr size_t FOUR(){
	return 4; 
}

void foo(){
	static std::string s1 = "abcd";
	std::string_view s2{s1};
	const char *p = getStrPtr();
	std::string s3 = p;
	static std::string s4("abcd", FOUR());
	std::string_view s5{s1}, s6{s4};
}

Проверки s1 и s4 фиксируются, объявляя их как static. Проверки s2, s5 и s6 фиксируются, объявляя их как std::string_view объекты. Проверка на s3 фиксируется путем сохранения постоянных данных в символьном указателе.

Информация о результатах

Группа: Производительность
Язык: C++
По умолчанию: Откл.
Синтаксис командной строки: EXPENSIVE_CONSTANT_STD_STRING
Воздействие: среднее
Представлен в R2020b