Не тратьте впустую время и память
Причина
Это C++.
Примечание
Время и память, которые вы тратите с пользой для достижения цели (например, скорость разработки, безопасность ресурсов или упрощение тестирования), не являются напрасными. «Ещё одним преимуществом стремления к эффективности является то, что этот процесс заставляет вас понимать проблему глубже.» — Алекс Степанов
Пример (плохой)
struct X {
char ch;
int i;
string s;
char ch2;
X& operator=(const X& a);
X(const X&);
};
X waste(const char* p)
{
if (!p) throw Nullptr_error{};
int n = strlen(p);
auto buf = new char[n];
if (!buf) throw Allocation_error{};
for (int i = 0; i < n; ++i) buf[i] = p[i];
// ... манипулируем буфером ...
X x;
x.ch = 'a';
x.s = string(n); // выделяем x.s место для *p
for (gsl::index i = 0; i < x.s.size(); ++i) x.s[i] = buf[i]; // копируем buf в x.s
delete[] buf;
return x;
}
void driver()
{
X x = waste("Typical argument");
// ...
}
Да, это карикатура, но мы видели каждую отдельную ошибку в промышленном коде, и хуже. Обратите внимание, что расположение полей X гарантирует потерю минимум 6 байт (скорее всего, больше). Ложное определение операций копирования отключает семантику перемещения, поэтому операция возврата медленная (обратите внимание, что оптимизация возвращаемого значения, RVO, здесь не гарантирована). Использование new и delete для buf избыточно; если нам действительно нужна локальная строка, следует использовать локальный string. Здесь ещё несколько ошибок производительности и ненужных усложнений.
Пример (плохой)
void lower(zstring s)
{
for (int i = 0; i < strlen(s); ++i) s[i] = tolower(s[i]);
}
Это фактически пример из промышленного кода. Мы видим, что в условии цикла используется i < strlen(s). Это выражение будет вычисляться на каждой итерации цикла, что означает, что strlen должна обходить строку при каждом проходе для определения её длины. Поскольку содержимое строки меняется, предполагается, что tolower не влияет на длину строки, поэтому лучше кэшировать длину вне цикла и не нести эти расходы на каждой итерации.
Примечание
Отдельный пример расточительности редко бывает значимым, и там, где он значим, он, как правило, легко устраняется экспертом. Однако расточительность, щедро разбросанная по всей кодовой базе, может легко стать значимой, а эксперты не всегда так доступны, как хотелось бы. Цель этого правила (и поддерживающих его более конкретных правил) — устранить большинство потерь, связанных с использованием C++, до того как они возникнут. После этого мы можем посмотреть на потери, связанные с алгоритмами и требованиями, но это выходит за рамки данных руководящих принципов.
Контроль
Многие более конкретные правила направлены на общие цели простоты и устранения ненужных потерь.
- Помечать неиспользуемое возвращаемое значение пользовательского не-дефолтного постфиксного
operator++илиoperator--. Предпочитайте вместо этого префиксную форму. (Примечание: «пользовательский не-дефолтный» призван уменьшить шум. Пересмотрите это правило применения, если оно всё ещё создаёт слишком много шума на практике.)