Хорошо ли определять переменную внутри цикла? [закрыто]

15

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

Каковы недостатки этого?

Может ли кто-нибудь объяснить это мне?

user3260672
источник
7
Какой язык программирования преподавал ваш инструктор?
Брайан
2
Если вы определяете переменную с непримитивным типом в цикле, ваша программа может в конечном итоге без необходимости вызывать свой конструктор каждый раз через цикл. Если вам нужно определить его только один раз за пределами цикла, сделайте это.
Брандин
17
Когда у вас возникает путаница в отношении того, что говорит инструктор, лучший ресурс спрашивает инструктора. Они могут дать вам плотную обратную связь, которую не может предоставить сайт вопросов и ответов.
1
Межсайтовый дубликат: Разница между объявлением переменных до или в цикле? (и, конечно, много, много дубликатов на этом сайте для такого элементарного вопроса (включая те, которые касаются только C ++)).
Питер Мортенсен
2
Этот совет был конкретным для контекста. Что касается личного стиля, я предпочитаю объявлять свои переменные, constесли нет причин для этого (привычка функционального программирования). Либо я не буду изменять их, и оптимизатор должен определять, когда они не нужны, либо я сделаю это, и я предотвратил серьезную ошибку. Когда эти постоянные промежуточные значения являются специфическими для итерации цикла, это означает их объявление внутри цикла. Однако в другой раз вам нужно объявить переменные вне цикла, когда вы будете ссылаться на них вне цикла; например, результаты, которые вы храните.
Дэвислор

Ответы:

42

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

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

Уточнить: пока compute() всегда возвращает одно и то же значение, это

int value = compute();
while (something) {
    doSomething(value);
}

умнее, чем это:

while (something) {
    int value = compute();
    doSomething(value);
}
Килиан Фот
источник
2
Как бы вы определили переменную в цикле и присвоили ее перед циклом?
Человек в маске
6
@MaskedMan, я думаю, что вы неправильно понимаете. Килиан имел в виду, что если у вас есть переменная, которой присваивается одно и то же значение во время каждой итерации цикла, например, для одной и той же переменной даты задано значение 1/1/1900, переменная должна быть объявлена, а значение должно быть назначено до цикла.
ps2goat
2
Я не думаю, что за последние двадцать лет был написан компилятор (за исключением курса по компиляции старшекурсников), который не мог бы понять, что вы присваиваете одно и то же значение на каждой итерации, и переносить это назначение из цикла.
TMN
14
@tmn: Никогда не позволяйте компилятору делать то, что вы можете сделать самостоятельно, с большей ясностью кода.
Роберт Харви,
10
@ TMN, не обязательно. Такая оптимизация возможна только в том случае, если компилятор может доказать, что вычисления не имеют побочных эффектов.
Пол Дрэйпер
16

Сложные типы имеют нетривиальные конструкторы и деструкторы.

Они будут вызваны в начале и в конце тела цикла (так как он инициализируется и выходит из области видимости). Если инициализация дорогая, так как ей нужно выделить немного памяти, этого следует избегать.

Однако для тривиальных типов это не проблема. Само выделение и освобождение - это просто сложение и вычитание значения из указателя стека. (который будет оптимизирован)

чокнутый урод
источник
спасибо, именно тот ответ, который я искал!
gebbissimo
6

Ну, его совет немного слишком прост (это преуменьшение).
Следование за ним простирается от хорошей идеи о том, кому небезразлично, а плохой до невозможной .

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

    #include <iostream>
    #include <string>
    
    int main() {
        std::string s; // Don't needlessly free the buffer
        while ((std::cin >> s))
            std::cout << s;
    }
  2. Вы должны избегать этого как вопрос стиля, когда это не имеет значения для производительности.

    #include <stdio.h>
    #include <stdlib.h>
    int f(int, int);
    
    int main() {
        for (int i = 0; i < 100; ++i) {
            int x = rand(); // Declared here so you don't need to hunt it down.
            printf("%d => %d\n", x, f(x-1, x+i));
        }
    }
  3. Вы действительно должны избегать его, когда он имеет худшую производительность или неправильную семантику.

    #include <iostream>
    #include <string>
    std::string generate(int);
    
    int main() {
        for(int i = 0; i < 100; ++i) {
            std::string s = generate(i); // Using copy-ellision here
            std::cout << s;
        }
    }
  4. Вы не можете следовать этому, когда используемый тип не допускает ни подкачки, ни перемещения, ни копирования.

    #include <iostream>
    #include <puzzle>
    
    int main() {
        for (int i = 0; i < 100; ++i) {
            Puzzle x(i); // Puzzle is an immutable class. For whatever reasons.
            std::cout << x;
        }
    }
Deduplicator
источник
2
В зависимости от вашего определения «в цикле», 1 может быть изменено for (std::string s; std::cin >> s;) ...и оставаться «снаружи»
Caleth