Μετάβαση στο περιεχόμενο

Έλεγχος ροής & βρόχων

Κατακτήστε όλες τις δομές ροής ελέγχου C++, συμπεριλαμβανομένων των if/else, switch, όλων των μορφών βρόχου, βάσει εύρους για, αρχικοποιητές C++17 και δομημένες συνδέσεις.

Έλεγχος ροής στο C++

Οι δηλώσεις ροής ελέγχου καθορίζουν τη σειρά με την οποία εκτελείται το πρόγραμμά σας. Το C++ παρέχει ένα πλούσιο σύνολο κατασκευών ροής ελέγχου που ξεπερνούν πολύ τα βασικά που κληρονομήθηκαν από το C. Το σύγχρονο C++ (C++17 και πέρα) προσθέτει χαρακτηριστικά όπως τοifμε αρχικοποιητές και τοconstexpr ifπου κάνουν τη ροή ελέγχου πιο εκφραστική και ασφαλέστερη.

Μια βασική αρχή: ελαχιστοποιήστε το εύρος των μεταβλητών. Κάθε μεταβλητή θα πρέπει να δηλώνεται όσο το δυνατόν πιο κοντά στην πρώτη της χρήση και στο στενότερο δυνατό πεδίο εφαρμογής. Οι δυνατότητες του C++17 όπως τοifμε αρχικοποιητές σάς βοηθούν να ακολουθήσετε αυτήν την αρχή.

if/else και αλλάξτε

Οι δηλώσειςifκαιswitchείναι οι κύριοι μηχανισμοί διακλάδωσης. Το C++17 προσθέτει ρήτρες αρχικοποιητή και στα δύο:

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";
    }
}
Έξοδος

Πατήστε Εκτέλεση για να μεταγλωττίσετε και να εκτελέσετε αυτό το παράδειγμα.

Loops: for, while, do-while, and range-based for

C++ offers multiple loop forms. The range-based for loop (C++11) is preferred when iterating over containers because it eliminates off-by-one errors and is clearer:

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);
}
Έξοδος

Πατήστε Εκτέλεση για να μεταγλωττίσετε και να εκτελέσετε αυτό το παράδειγμα.

Structured Bindings (C++17)

Structured bindings (C++17) allow you to decompose an object into its constituent parts. They work with arrays, structs with all public members,std::pair,std::tuple, and any type that supports the structured bindings protocol.

The syntaxauto [a, b] = expr;creates named references to the components ofexpr. You can addconstand&qualifiers:const auto& [a, b] = expr;creates const references without copying.

Structured bindings are especially powerful withstd::mapiteration (decomposingstd::pair), function returns that usestd::pairorstd::tuple, and theinsertmethod's return value on associative containers.

Βέλτιστη πρακτική

Usebreakto exit a loop early when you have found what you need. Usecontinueto skip the rest of the current iteration and move to the next one. Both make loops easier to understand when used judiciously.

Prefer range-based for over index-based for when you do not need the index. It eliminates entire classes of bugs: off-by-one errors, using the wrong variable as index, and signed/unsigned comparison warnings.

When you do need the index, consider usingstd::size_tas the loop variable type to match the return type of.size(), or usestd::ssize()(C++20) which returns a signed size.

Avoid deeply nested loops — if you find yourself nesting three or more loops, extract the inner loops into functions. This improves readability and testability.

  • Προτιμήστε τους βρόχους βάσει εύρους έναντι των βρόχων που βασίζονται σε ευρετήριο όταν το ευρετήριο δεν χρειάζεται
  • Use const auto& in range-based for to avoid unnecessary copies
  • Χρησιμοποιήστε δομημένες συνδέσεις για να αποσυνθέσετε ζεύγη και πλειάδες σε μεταβλητές βρόχου
  • Εξαγάγετε τους βαθιά ένθετους βρόχους σε επώνυμες συναρτήσεις για σαφήνεια

constexpr if (C++17): Διακλάδωση μεταγλώττισης-χρόνου

Το if constexpr αξιολογεί τη συνθήκη κατά το χρόνο μεταγλώττισης και απορρίπτει τον κλάδο που δεν έχει ληφθεί. Αυτό είναι απαραίτητο στον προγραμματισμό προτύπων όπου διαφορετικοί κλάδοι μπορεί να μην ισχύουν καν για όλους τους τύπους:

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)
}
Έξοδος

Πατήστε Εκτέλεση για να μεταγλωττίσετε και να εκτελέσετε αυτό το παράδειγμα.

Βασικά συμπεράσματα
  • C++17 if/switch με αρχικοποιητές περιορίζει το εύρος μεταβλητών στη δήλωση — χρησιμοποιήστε τα για να διατηρήσετε τον κώδικα καθαρό
  • Οι βρόχοι βάσει εύρους εξαλείφουν τα τυχαία σφάλματα και τα προβλήματα σύγκρισης με υπογραφή/μη υπογραφή
  • Οι δομημένες δεσμεύσεις (C++17) αποσυνθέτουν ζεύγη, πλειάδες και δομές σε ονομασμένες μεταβλητές
  • Χρησιμοποιήστε το [[fallthrough]] για να τεκμηριώσετε σκόπιμη πτώση θήκης
  • εάν το constexpr (C++17) αξιολογεί τις συνθήκες κατά το χρόνο μεταγλώττισης — ο κλάδος που απορρίφθηκε δεν έχει μεταγλωττιστεί
  • Ελαχιστοποίηση εμβέλειας μεταβλητής: δηλώστε τις μεταβλητές όσο το δυνατόν πιο κοντά στην πρώτη χρήση

Κουίζ — Δοκιμάστε τις γνώσεις σας

(15 XP)

1. Ποιο είναι το πλεονέκτημα του`if`του C++17 με αρχικοποιητή:`if (auto x = f(); x > 0)`;

2. Τι κάνει το`for (const auto& [key, val] : my_map)`;

3. Τι συμβαίνει με τον κλάδο που απορρίφθηκε μιας πρότασης`if constexpr`;