2010-11-23 2 views
45

Можно создать дубликат:
Is there a performance difference between i++ and ++i in C++?++ i или i ++ in для циклов ??

Есть причина, некоторые программисты пишут ++i в нормальный цикл вместо написания i++?

+0

@djacobson: ну, вы можете искать YouTube для этого и, вероятно, узнать, сколько ангелов могут танцевать на голове булавки одновременно. Но я имел в виду, что если «экстраординарные требования требуют экстраординарного доказательства», то выбор предварительного приращения практически не требует никаких оснований. Это совершенно необычно, особенно по сравнению с альтернативой ... – 2010-11-23 23:16:40

+0

Это, должно быть, задавали и отвечали сто раз до этого. – 2010-11-24 00:08:33

+1

@ Мартин Йорк: что странно, так это то, что люди по-прежнему не хотят отвечать. – 2010-11-24 07:10:18

ответ

64

Для целых чисел нет разницы между пре-и пост-приращением.

Если i является объектом нетривиального класса, то ++i обычно является предпочтительным, так как объект изменяется, и затем оценивал, в то время как i++ модифицирует после оценки, таким образом, требует копии быть сделано.

0

Индивидуальные предпочтения.

Обычно. Иногда это имеет значение, но не для того, чтобы казаться рывком здесь, но если вы должны спросить, возможно, нет.

1

Нет компилятор на вес соли будет работать по-разному между

for(int i=0; i<10; i++) 

и

for(int i=0;i<10;++i) 

++ я и ++ имеют ту же стоимость. Единственное отличие заключается в том, что возвращаемое значение ++ i равно i + 1, тогда как возвращаемое значение i ++ - это i.

Так что для тех, кто предпочитает ++ i, вероятно, нет обоснованного оправдания, а также личных предпочтений.

РЕДАКТИРОВАТЬ: Это неправильно для классов, как сказано в каждом другом сообщении. i ++ будет генерировать копию, если i является классом.

+1

Это, вероятно, верно для индексов типа int, но не в целом для итераторов и других объектов, которые могут выступать в качестве индексов. – 2010-11-23 22:43:03

+0

не i ++ и ++ я называю одним и тем же оператором? – rtpg 2010-11-23 22:45:25

+1

Нет, `++ i` вызывает` i.operator ++() `и` i ++ `вызывает` i.operator (int) `. См. [Мой ответ] (http://stackoverflow.com/questions/4261708/i-or-i-in-for-loops/4261836#4261836) для деталей реализации. – fredoverflow 2010-11-23 22:57:41

0

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

6

++i - это предварительный приращение; i++ - это приращение.
Недостатком пост-приращения является то, что он генерирует дополнительное значение; он возвращает копию старого значения при изменении i. Таким образом, вы должны избегать его, когда это возможно.

81

++i немного более эффективным благодаря своей семантике:

++i; // Fetch i, increment it, and return it 
i++; // Fetch i, copy it, increment i, return copy 

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

В качестве примера, рассмотрим следующий цикл, используя теоретический класс BigInteger обеспечения произвольной точности целых чисел (и, таким образом, своего рода вектор-подобный внутренностей):

std::vector<BigInteger> vec; 
for (BigInteger i = 0; i < 99999999L; i++) { 
    vec.push_back(i); 
} 

Это я ++ операция включает в себя конструкцию копии (т.е.оператор новый, цифровая копия) и уничтожение (оператор delete) для цикла, который не будет делать ничего больше, чем по существу сделать еще одну копию объекта индекса. По сути, вы удвоили выполняемую работу (и, скорее всего, увеличили фрагментацию памяти), просто используя приращение постфикса, где префикс был бы достаточным.

2

Существует причина для этого: производительность. i ++ генерирует копию, и это отходы, если вы сразу ее отбросите. Конечно, компилятор может оптимизировать эту копию, если i является примитивным, но он не может, если это не так. См. Вопрос this.

4

С целыми числами это предпочтение.

Если переменная цикла является классом/объектом, это может иметь значение (только профилирование может рассказать вам, является ли это существенной разницей), поскольку для версии с последующим увеличением требуется, чтобы вы создали копию этого объекта, который отбрасывается ,

Если создание этой копии является дорогостоящей операцией, вы платите этот расход один раз за каждый раз, когда вы проходите цикл, без всякой причины.

Если вы привыкли всегда использовать ++i для петель, вам не нужно останавливаться и думать о том, имеет ли смысл то, что вы делаете в этой конкретной ситуации. Вы просто всегда.

1

Как уже отмечалось, предварительное приращение обычно быстрее, чем пост-инкремент для пользовательских типов. Чтобы понять, почему это так, посмотрим на типичный шаблон кода для реализации обоих операторов:

Foo& operator++() 
{ 
    some_member.increase(); 
    return *this; 
} 

Foo operator++(int dummy_parameter_indicating_postfix) 
{ 
    Foo copy(*this); 
    ++(*this); 
    return copy; 
} 

Как вы можете видеть, версия префикс просто изменяет объект и возвращает его по ссылке.

С другой стороны, постфиксная версия должна сделать копию до того, как будет выполнен фактический шаг, а затем копия будет скопирована обратно на вызывающего абонента по значению. Из исходного кода очевидно, что постфиксная версия должна выполнять больше работы, поскольку она включает в себя вызов префиксной версии: ++(*this);

Для встроенных типов это не имеет никакого значения, если вы отбрасываете значение , то есть до тех пор, пока вы не вставляете ++i или i++ в более крупное выражение, такое как a = ++i или b = i++.