Чи добре визначити змінну всередині циклу? [зачинено]


15

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

Які недоліки в цьому?

Чи може мені це пояснити будь-яке тіло?


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;
        }
    }

2
Залежно від вашого визначення поняття "в циклі", 1 можна змінити на for (std::string s; std::cin >> s;) ...та все ще бути "зовні"
Caleth
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.