Инкремент в C ++ - Когда использовать x ++ или ++ x? - PullRequest
76 голосов
/ 28 ноября 2009

В настоящее время я изучаю C ++, и я узнал об увеличении некоторое время назад. Я знаю, что вы можете использовать "++ x", чтобы сделать приращение до, и "x ++", чтобы сделать это после.

Тем не менее, я действительно не знаю, когда использовать любой из двух ... Я никогда не использовал "++ x", и до сих пор все работало нормально - так, когда мне его использовать? *

Пример. В цикле for предпочтительнее использовать "++ x"?

Кроме того, может ли кто-нибудь объяснить, как именно работают различные увеличения (или уменьшения)? Я был бы очень признателен.

Ответы [ 10 ]

97 голосов
/ 28 ноября 2009

Это не вопрос предпочтения, а логика.

x++ увеличивает значение переменной x после обработки текущего оператора.

++x увеличивает значение переменной x до обработки текущего оператора.

Так что просто определитесь с логикой, которую вы пишете.

x += ++i увеличит i и добавит i + 1 к x. x += i++ добавит i к x, затем увеличит i.

43 голосов
/ 28 ноября 2009

Скотт Мейерс говорит, что вы предпочитаете префикс, за исключением тех случаев, когда логика диктует, что постфикс является подходящим.

«Более эффективный C ++», пункт № 6 - это достаточно авторитет для меня.

Для тех, кто не владеет книгой, вот соответствующие цитаты. Со страницы 32:

Из ваших дней программирования на C вы можете вспомнить, что префиксную форму оператора приращения иногда называют «приращением и извлечением», тогда как постфиксную форму часто называют «извлечением и приращением». Эти две фразы важно помнить, потому что они почти действуют как формальные спецификации ...

А на странице 34:

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

19 голосов
/ 28 ноября 2009

С cppreference при увеличении итераторов:

Вы должны предпочесть предварительное увеличение оператор (++ iter) к постинкременту оператор (iter ++), если вы не собираетесь использовать старое значение. Пост-инкремент обычно реализуется следующим образом:

   Iter operator++(int)   {
     Iter tmp(*this); // store the old value in a temporary object
     ++*this;         // call pre-increment
     return tmp;      // return the old value   }

Очевидно, это менее эффективно, чем Преинкремент.

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

6 голосов
/ 28 ноября 2009

Я просто хочу заметить, что сгенерированный код отключен, если вы используете увеличение до / после, когда семантика (до / после) не имеет значения.

пример:

pre.cpp:

#include <iostream>

int main()
{
  int i = 13;
  i++;
  for (; i < 42; i++)
    {
      std::cout << i << std::endl;
    }
}

post.cpp:

#include <iostream>

int main()
{

  int i = 13;
  ++i;
  for (; i < 42; ++i)
    {
      std::cout << i << std::endl;
    }
}

_

$> g++ -S pre.cpp
$> g++ -S post.cpp
$> diff pre.s post.s   
1c1
<   .file   "pre.cpp"
---
>   .file   "post.cpp"
5 голосов
/ 29 ноября 2009

Самое важное, что нужно иметь в виду, imo, - это то, что x ++ должен вернуть значение до того, как фактически произойдет приращение, поэтому он должен сделать временную копию объекта (предварительное приращение). Это менее эффективно, чем ++ x, который увеличивается на месте и возвращается.

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

for (int i(0);i<10;++i)
for (int i(0);i<10;i++)
2 голосов
/ 28 ноября 2009

Просто хотел еще раз подчеркнуть, что ++ x, как ожидается, будет на быстрее , чем x ++ (особенно если x является объектом произвольного типа), поэтому, если это не требуется по логическим причинам, ++ x должен быть использован.

2 голосов
/ 28 ноября 2009

Я согласен с @BeowulfOF, хотя для ясности я бы всегда выступал за разделение высказываний так, чтобы логика была абсолютно ясной, т.е.

i++;
x += i;

или

x += i;
i++;

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

1 голос
/ 28 ноября 2009

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

Важное отличие при работе с итераторами STL (которые также реализуют эти операторы) состоит в том, что он ++ создает копию объекта, на который указывает итератор, затем увеличивает его, а затем возвращает копию. ++ он, с другой стороны, сначала выполняет приращение, а затем возвращает ссылку на объект, на который теперь указывает итератор. Это в основном актуально, когда важен каждый бит производительности или когда вы реализуете свой собственный STL-итератор.

Редактировать: исправлено смешение префикса и суффикса

0 голосов
/ 07 ноября 2015

Постфиксная форма ++, - оператор следует правилу use-then-change ,

Форма префикса (++ x, - x) соответствует правилу изменить-затем-использовать .

Пример 1:

Когда несколько значений каскадируются с помощью << using <em>cout , тогда вычисления (если таковые имеются) производятся справа налево, а печать - слева направо, например, (если val если изначально 10)

 cout<< ++val<<" "<< val++<<" "<< val;

приведет к

12    10    10 

Пример 2:

В Turbo C ++, если в выражении найдено несколько вхождений ++ или (в любой форме), сначала вычисляются все префиксные формы, затем вычисляется выражение и, наконец, вычисляются постфиксные формы, например,

int a=10,b;
b=a++ + ++a + ++a + a;
cout<<b<<a<<endl;

Вывод в Turbo C ++ будет

48 13

Принимая во внимание, что он будет выводиться в современных компиляторах дня (потому что они строго следуют правилам)

45 13
  • Примечание: многократное использование операторов увеличения / уменьшения для одной и той же переменной в одном выражении не рекомендуется. Обработка / результаты таких
    выражения варьируются от компилятора к компилятору.
0 голосов
/ 02 ноября 2014

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

char a[256] = "Hello world!";
char b[256];
int i = 0;
do {
  b[i] = a[i];
} while (a[i++]);

Мы хотим, чтобы цикл выполнялся, встречая нулевой символ (который проверяет ложь) в конце строки. Это требует тестирования предварительного увеличения значения, а также увеличения индекса. Но не обязательно в таком порядке - способ кодирования с предварительным приращением будет:

int i = -1;
do {
  ++i;
  b[i] = a[i];
} while (a[i]);

Это вопрос вкуса, который яснее, и если у машины имеется несколько регистров, оба должны иметь одинаковое время выполнения, даже если [i] является дорогой или имеет побочные эффекты функцией. Существенной разницей может быть выходное значение индекса.

...