Инкремент в C++ - Когда использовать x++ или ++x?

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

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

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

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

12 ответов

Решение

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

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

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

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

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

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

Пункт 6 "Более эффективный C++" - это достаточный авторитет для меня.

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

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

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

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

Из 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   }

Очевидно, это менее эффективно, чем предварительное увеличение.

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

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

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

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

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

пример:

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"

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

i++;
x += i;

или же

x += i;
i++;

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

Если count{5};

Если вы используете ++count, он будет обработан перед оператором

      total = --count +6;

Итого будет равно 10

Если вы используете count++, он будет обработан после оператора

      total = count-- +6;

Итого будет равно 11

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

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

Пример 1:

Когда несколько значений каскадируются с помощью << 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
  • Примечание. Многократное использование операторов увеличения / уменьшения для одной и той же переменной в одном выражении не рекомендуется. Обработка / результаты такого
    выражения варьируются от компилятора к компилятору.

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

Вы просили привести пример:

Этот ( order это std ::vector) произойдет сбой для i == order.size()-1 по доступу:

      while(i++ < order.size() && order[i].size() > currLvl);

Это не приведет к сбою в order[i].size(), в качестве i будет увеличиваться, проверяться и цикл будет завершен:

      while(++i < order.size() && order[i].size() > currLvl);

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

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

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

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

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] является дорогой функцией или имеет побочные эффекты. Существенной разницей может быть выходное значение индекса.

Другие вопросы по тегам