Предлагаемое улучшение скорости при определении строки со значением сразу, вместо задержки - PullRequest
10 голосов
/ 18 января 2012

В настоящее время я читаю «Язык программирования C ++: специальное издание» Бьярна Страуструпа, и на странице 133 говорится следующее:

Для пользовательских типов откладывается определениепеременная, пока не доступен подходящий инициализатор, также может привести к повышению производительности.Например:

string s;  /* .... */ s = "The best is the enemy of the good.";

может быть намного медленнее, чем

string s = "Voltaire";

Я знаю, что может легко , чтоозначает, что это не обязательно так, однако давайте просто скажем, что это происходит.

Почему это может привести к потенциальному повышению производительности?

Так ли это только с пользователем-определенные типы (или даже типы STL) или это также относится к int, float и т. д.

Ответы [ 7 ]

9 голосов
/ 18 января 2012

Я бы сказал, что это в основном типы с нетривиальными конструкторами по умолчанию, по крайней мере, что касается производительности.

Разница между этими двумя подходами заключается в том, что:

  • В первой версии сначала создается пустая строка (с использованием конструктора по умолчанию); затем оператор присваивания используется для эффективного отбрасывания работы, выполняемой конструктором по умолчанию, и для присвоения нового значения строке.
  • Во втором варианте необходимое значение устанавливается сразу же, в точке построения.

Конечно, очень сложно априори сказать, насколько велика разница в производительности.

7 голосов
/ 18 января 2012
  1. Требуется время, чтобы выполнил конструктор по умолчанию .Переопределение того, к чему она инициализировала строку в последующем вызываемом операторе присваивания , также требует времени.

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

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

1 голос
/ 18 января 2012

Потому что:

string s;  /* .... */ s = "The best is the enemy of the good.";    

Включает две операции: строительство и назначение

Пока:

string s = "Voltaire";   

Включает только строительство.

Это эквивалентно выбору Инициализатор элементов выводит список назначений в теле конструктора .

0 голосов
/ 18 января 2012

Почему это может увеличить производительность?

Первый случай включает в себя инициализацию по умолчанию, за которой следует присвоение; вторая включает в себя инициализацию от значения. Инициализация по умолчанию может выполнять некоторую работу, которую впоследствии придется переделывать (или даже отменять) по присваиванию, поэтому первый случай может потребовать больше работы, чем второй.

Так ли это с пользовательскими типами (или даже с типами STL) или же с int, float и т. Д.?

Это только так с пользовательскими типами; и тогда это зависит от того, что на самом деле делают конструкторы и оператор присваивания. Для скалярных типов инициализация по умолчанию ничего не делает, а присваивание делает то же самое, что и инициализация из значения, поэтому обе альтернативы будут эквивалентны.

0 голосов
/ 18 января 2012

Рассмотрим, что происходит в обоих случаях. В первом случае:

  • конструктор по умолчанию для "s"
  • оператор присваивания вызван для "s"

Во втором случае сначала рассмотрим, что с разрешением копирования это эквивалентно string s("Voltaire"), таким образом:

  • конструктор c-строки с именем

Логически первый подход требует, чтобы абстрактная машина выполняла больше работы. Переводит ли это на самом деле более реальный код, зависит от фактического типа и того, сколько оптимизатор может сделать. Обратите внимание, что для всех типов пользователей, кроме тривиальных, оптимизатор может предположить, что конструктор по умолчанию имеет побочные эффекты, поэтому просто не может его удалить.

Эта дополнительная стоимость должна применяться только к пользовательским типам, так как стоимость указана в конструкторе по умолчанию. Для любого примитивного типа, такого как int, или фактически для любого с тривиальным конструктором / копией, конструктор по умолчанию не требует затрат - данные просто не будут инициализированы (если они находятся в области действия функции).

0 голосов
/ 18 января 2012

Хороший вопрос. Вы правы, это происходит только со сложными типами. То есть классы и структуры, std :: string такой объект. Реальная проблема, связанная с этим, связана с конструктором.

Когда объект создан, то есть

std::string s;

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

Позже вы делаете:

s = "hello world!";

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

На самом деле это сводится к одной операции, если вы задаете значение, когда переменная определена, т. Е .:

std::string s = "Hello world";

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

std::string s("Hello world");

Надеюсь, это помогло немного прояснить ситуацию.

0 голосов
/ 18 января 2012

В классе есть три способа инициализации строки:

string  s;         // Default constructor
string  s = "..."; // Default constructor followed by operator assignment
string  s("...");  // Constructor with parameters passed in

Строковый класс должен выделять память. Лучше выделять его, когда он знает, сколько памяти ему нужно.

...