Sari la conținut

Controlul fluxului și buclelor

Stăpânește toate structurile fluxului de control C++, inclusiv if/else, switch, toate formele de buclă, for bazate pe intervale, inițializatorii C++17 și legăturile structurate.

Controlul fluxului în C++

Instrucțiunile fluxului de control determină ordinea în care se execută programul. C++ oferă un set bogat de structuri de flux de control care depășesc cu mult elementele de bază moștenite de la C. C++ modern (C++17 și mai departe) adaugă funcții precumifcu inițializatoare șiconstexpr ifcare fac fluxul de control mai expresiv și mai sigur.

Un principiu cheie: minimizați sfera variabilelor. Fiecare variabilă trebuie declarată cât mai aproape posibil de prima utilizare și în cel mai restrâns domeniu posibil. Caracteristicile C++17 precumifcu inițializatoare vă ajută să urmați acest principiu.

dacă/altfel și comută

Instrucțiunileifșiswitchsunt mecanismele primare de ramificare. C++17 adaugă clauze de inițializare la ambele:

control_flow.cppc++23
#include <iostream>
#include <map>
#include <string>
#include <optional>

std::optional<int> find_value(const std::string& key) {
    std::map<std::string, int> data = {{"alpha", 1}, {"beta", 2}, {"gamma", 3}};
    if (auto it = data.find(key); it != data.end()) {  // C++17 if with initializer
        return it->second;
    }
    return std::nullopt;
}

int main() {
    // C++17: if with initializer — the variable's scope is limited to the if/else
    if (auto val = find_value("beta"); val.has_value()) {
        std::cout << "Found: " << *val << '\n';
    } else {
        std::cout << "Not found\n";
    }
    // 'val' is no longer in scope here — cleaner!

    // switch with C++17 initializer
    enum class Color { Red, Green, Blue };
    Color c = Color::Green;

    switch (auto name = "unknown"; c) {  // C++17 switch with initializer
        case Color::Red:   name = "red";   std::cout << name << '\n'; break;
        case Color::Green: name = "green"; std::cout << name << '\n'; break;
        case Color::Blue:  name = "blue";  std::cout << name << '\n'; break;
    }

    // [[fallthrough]] attribute (C++17) — document intentional fallthrough
    int priority = 2;
    switch (priority) {
        case 1:
            std::cout << "Critical: ";
            [[fallthrough]];       // Tells the compiler this is intentional
        case 2:
            std::cout << "Important: ";
            [[fallthrough]];
        case 3:
            std::cout << "Handle it\n";
            break;
        default:
            std::cout << "Unknown priority\n";
    }
}
Rezultat

Apasă Rulează pentru a compila și rula acest exemplu.

Bucle: for, while, do-while și bazate pe interval for

C++ oferă mai multe forme de buclă. Bucla for bazată pe gamă (C++11) este preferată atunci când se repetă peste containere, deoarece elimină erorile individuale și este mai clară:

loops.cppc++23
#include <iostream>
#include <vector>
#include <string>
#include <map>

int main() {
    std::vector<std::string> names = {"Alice", "Bob", "Charlie"};

    // Traditional for loop — use when you need the index
    for (std::size_t i = 0; i < names.size(); ++i) {
        std::cout << i << ": " << names[i] << '\n';
    }

    // Range-based for loop (C++11) — preferred for iteration
    for (const auto& name : names) {        // const auto& avoids copying
        std::cout << name << '\n';
    }

    // Range-based for with structured bindings (C++17)
    std::map<std::string, int> scores = {{"Alice", 95}, {"Bob", 87}, {"Charlie", 92}};
    for (const auto& [name, score] : scores) {   // Destructure the pair
        std::cout << name << " scored " << score << '\n';
    }

    // C++20: range-based for with init-statement
    for (auto v = std::vector{1, 2, 3, 4, 5}; auto elem : v) {
        std::cout << elem << ' ';
    }
    std::cout << '\n';

    // while loop — use when the number of iterations is unknown
    int n = 1;
    while (n <= 1024) {
        std::cout << n << ' ';
        n *= 2;
    }
    std::cout << '\n';

    // do-while — body executes at least once
    int input;
    do {
        std::cout << "Enter a positive number: ";
        input = 42;  // Simulated input for example
    } while (input <= 0);
}
Rezultat

Apasă Rulează pentru a compila și rula acest exemplu.

Legături structurate (C++17)

Legăturile structurate (C++17) vă permit să descompuneți un obiect în părțile sale constitutive. Ele lucrează cu matrice, structuri cu toți membrii publici,std::pair,std::tupleși orice tip care acceptă protocolul de legături structurate.

Sintaxaauto [a, b] = expr;creează referințe denumite la componenteleexpr. Puteți adăuga calificativeconstși&:const auto& [a, b] = expr;creează referințe const fără a copia.

Legăturile structurate sunt deosebit de puternice cu iterațiastd::map(descompunereastd::pair), returnările de funcții care utilizeazăstd::pairsaustd::tupleși valoarea de returnare a metodeiinsertpe containerele asociative.

Bună practică

Utilizațibreakpentru a părăsi o buclă mai devreme când ați găsit ceea ce aveți nevoie. Utilizațicontinuepentru a sări peste restul iterației curente și a trece la următoarea. Ambele fac buclele mai ușor de înțeles atunci când sunt utilizate în mod judicios.

Preferați bazat pe interval pentru decât pe index pentru atunci când nu aveți nevoie de index. Elimină clase întregi de erori: erori de fiecare dată, folosirea variabilei greșite ca index și avertismente de comparație semnate/nesemnate.

Când aveți nevoie de index, luați în considerare utilizareastd::size_tca tip de variabilă de buclă pentru a se potrivi cu tipul de returnare.size()sau utilizațistd::ssize()(C++20) care returnează o dimensiune semnată.

Evitați buclele profund imbricate - dacă vă aflați că înglobează trei sau mai multe bucle, extrageți buclele interioare în funcții. Acest lucru îmbunătățește lizibilitatea și testabilitatea.

  • Preferați buclele bazate pe intervale decât buclele bazate pe index atunci când indexul nu este necesar
  • Utilizați const auto& în funcție de interval pentru a evita copiile inutile
  • Utilizați legături structurate pentru a descompune perechile și tuplurile în variabilele buclei
  • Extrageți bucle profund imbricate în funcții denumite pentru claritate

constexpr if (C++17): Ramificare în timp de compilare

if constexpr evaluează condiția în timpul compilării și elimină ramura care nu este luată. Acest lucru este esențial în programarea șablonului, unde diferite ramuri pot să nu fie valabile pentru toate tipurile:

constexpr_if.cppc++23
#include <iostream>
#include <type_traits>
#include <string>

template <typename T>
void describe(T value) {
    std::cout << "Value: " << value;

    if constexpr (std::is_integral_v<T>) {
        // This branch only compiles when T is an integer type
        std::cout << " (integer, " << (value % 2 == 0 ? "even" : "odd") << ")";
    } else if constexpr (std::is_floating_point_v<T>) {
        // This branch only compiles when T is a floating-point type
        std::cout << " (floating-point)";
    } else {
        std::cout << " (other type)";
    }
    std::cout << '\n';
}

int main() {
    describe(42);           // Value: 42 (integer, even)
    describe(3.14);         // Value: 3.14 (floating-point)
    describe("hello");      // Value: hello (other type)
}
Rezultat

Apasă Rulează pentru a compila și rula acest exemplu.

Idei cheie
  • C++17 if/switch with initializers limit variable scope to the statement — use them to keep code clean
  • Buclele bazate pe intervale elimină erorile individuale și problemele de comparație semnate/nesemnate
  • Structured bindings (C++17) decompose pairs, tuples, and structs into named variables
  • Use [[fallthrough]] to document intentional switch case fallthrough
  • dacă constexpr (C++17) evaluează condițiile la momentul compilării — ramura eliminată nu este compilată
  • Minimizați domeniul de aplicare al variabilelor: declarați variabilele cât mai aproape posibil de prima utilizare

Test — Verifică-ți cunoștințele

(15 XP)

1. What is the advantage of C++17's`if`with initializer:`if (auto x = f(); x > 0)`?

2. Ce face`for (const auto& [key, val] : my_map)`?

3. Ce se întâmplă cu ramura aruncată a unei instrucțiuni`if constexpr`?