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

В настоящее время я читаю Bjarne Stroustrup на "Программе программирования С++: специальная версия", а на странице 133 указано следующее:

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

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

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

string s = "Voltaire";

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

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

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

Ответ 1

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

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

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

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

Ответ 2

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

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

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

Ответ 4

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

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

std::string s;

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

Позже вы выполните:

s = "hello world!";

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

Это фактически сводится к одной операции, если вы задаете значение, когда переменная определена, то есть:

std::string s = "Hello world";

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

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

Я надеюсь, что это помогло немного разобраться.

Ответ 5

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

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

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

  • конструктор c-string, называемый

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

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

Ответ 6

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

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

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

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

Ответ 7

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

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

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