ข้ามไปที่เนื้อหา

Ch5: Flow Control

TL;DR: Conditional Programming: C++ เตรียมคำสั่ง if, else, switch, และ Ternary Operator (? :) เพื่อใช้แบ่งแยกทิศทางการทำงานตามเงื่อนไขในขณะ Runtime หรือ Compile-time

⚡ Quick Reference

#include <iostream>

int main() {
    int a = 10;
    int b = 20;

    // if (condition) - ทำงานเมื่อเงื่อนไขประเมินค่าเป็นจริง (true)
    if (a < b) {
        std::cout << "a is less" << std::endl;
    }
    // if (!condition) - ตรวจสอบเงื่อนไขแบบกลับค่าตรรกะ
    if (!(a > b)) {
        // nested if - เงื่อนไขซ้อนเงื่อนไข
        if (a == 10) {
            std::cout << "a is 10" << std::endl;
        }
    }

    // if (condition) else - ทางเลือกสองฝั่งแบบย่อ
    if (a > b) {
        std::cout << "a > b" << std::endl;
    } else {
        std::cout << "a <= b" << std::endl;
    }

    // else if (condition) - เชื่อมโยงหลายเงื่อนไขที่เป็นอิสระต่อกัน
    if (a == b) {
        std::cout << "equal" << std::endl;
    } else if (a < b) {
        std::cout << "less" << std::endl;
    }

    // switch (expr) - ทางเลือกหลายฝั่งจากค่าจำนวนเต็มหรือ enum
    switch (a) {
        case 10:
            std::cout << "ten" << std::endl;
            break;
        default:
            break;
    }

    // (condition) ? expression1 : expression2 - ตัวดำเนินการ Ternary
    int max = (a > b) ? a : b;

    // if (init; condition) - ตัวแปรจำกัดสโคปในเงื่อนไข if (C++17)
    if (int temp = a + b; temp > 15) {
        std::cout << "temp > 15" << std::endl;
    }

    // switch (init; expr) - ตัวแปรจำกัดสโคปในเงื่อนไข switch (C++17)
    switch (int key = a * 2; key) {
        case 20:
            break;
    }

    // if constexpr (condition) - ประเมินและเลือกทางแยกเงื่อนไขในตอน Compile (C++17)
    if constexpr (sizeof(int) == 4) {
        std::cout << "32-bit int" << std::endl;
    }

    return 0;
}

🧠 Core Concepts

  • Conditional Programming: C++ เตรียมคำสั่ง if, else, switch, และ Ternary Operator (? :) เพื่อใช้แบ่งแยกทิศทางการทำงานตามเงื่อนไขในขณะ Runtime หรือ Compile-time
  • Short-Circuit Evaluation: ในตัวดำเนินการ && และ || การประเมินค่าจะหยุดทันทีเมื่อรู้ผลลัพธ์สุดท้ายแล้ว ทำให้ข้ามการประมวลผลคำสั่งหรือฟังก์ชันที่เหลือด้านหลัง
  • Scoped & Compile-Time Control: C++17 รองรับการประกาศตัวแปรเริ่มต้น (Scoped Initializer) ภายในโครงสร้าง if และ switch รวมถึงสนับสนุน if constexpr เพื่อเลือกประมวลผลโค้ดตั้งแต่ช่วง Compile-time

⚠️ Pitfalls (Quick Scan)

ข้อผิดพลาด วิธีแก้
การใช้เครื่องหมาย Assignment (=) แทนเครื่องหมาย Equality (==) ใช้ == ในการเปรียบเทียบเสมอ และเปิดแจ้งเตือน Compiler
ลืมใส่คำสั่ง break ในตอนท้ายของ Case ใน Switch ใส่ break; ท้ายบล็อกของแต่ละ Case เสมอ ยกเว้นกรณีตั้งใจให้เกิด Fallthrough
การใช้ชนิดข้อมูลอื่นที่ไม่ใช่ Integral/Enum ในคำสั่ง switch ใช้โครงสร้าง if-else if หรือแปลงค่านั้นให้เป็นจำนวนเต็ม/enum ก่อนใช้ Switch
ชนิดข้อมูลของผลลัพธ์ในตัวดำเนินการ Ternary ขัดแย้งกัน แปลงชนิดข้อมูลของทางเลือกทั้งสองให้ตรงกันก่อนเรียกใช้งาน
ลืมคำนึงถึงการขยายประเภทข้อมูลอัตโนมัติ (Implicit Promotion) ใน Ternary ระบุประเภทข้อมูลชัดเจนหรือใช้ auto ช่วยตรวจสอบผลลัพธ์การแปลงประเภทข้อมูล
การเรียกใช้ตัวแปรที่ถูกประกาศไว้ภายในบล็อก if/switch จากภายนอก ประกาศตัวแปรไว้ในสโคปที่กว้างขึ้นหากต้องการนำค่านั้นไปใช้ต่อด้านนอก
ประกาศสร้างตัวแปรในเคสหนึ่งแต่ไปเรียกใช้งานในอีกเคสหนึ่งของ Switch ใช้เครื่องหมายปีกกาครอบโค้ดแต่ละ Case เพื่อสร้าง Scope เฉพาะ
ลืมไปว่าการตรวจสอบเงื่อนไขจะหยุดทำงานที่เงื่อนไขแรกที่เป็นจริง เรียงลำดับเงื่อนไขให้ถูกต้อง หรือแยกเขียนเป็นคำสั่ง if อิสระต่อกัน
ใช้ Short-circuit กับฟังก์ชันที่มีผลกระทบข้างเคียง (Side-effect) เรียกทำงานฟังก์ชันและเก็บผลลัพธ์ลงตัวแปรก่อนนำไปเปรียบเทียบในเงื่อนไข
เขียนโค้ดที่ไวยากรณ์ผิดพลาดไว้ในสโคปของ if constexpr ที่โดนตัดทิ้ง ตรวจสอบให้แน่ใจว่าโค้ดทุกส่วนในฟังก์ชัน Template ถูกต้องตามไวยากรณ์เสมอ

📖 Full Details

Cause → Effect → Fix พร้อม timestamp (คลิกเพื่อดู) * **การใช้เครื่องหมาย Assignment (`=`) แทนเครื่องหมาย Equality (`==`)** -> **เงื่อนไขจะถูกประเมินตามค่าที่กำหนดให้ตัวแปร ทำให้บล็อกคำสั่งทำงานผิดพลาด** -> **ใช้ `==` ในการเปรียบเทียบเสมอ และเปิดแจ้งเตือน Compiler (09:58)** * **ลืมใส่คำสั่ง `break` ในตอนท้ายของ Case ใน Switch** -> **การทำงานจะไหลผ่านไปยังเคสถัดไปเรื่อยๆ จนกว่าจะเจอ Break หรือจบ Switch** -> **ใส่ `break;` ท้ายบล็อกของแต่ละ Case เสมอ ยกเว้นกรณีตั้งใจให้เกิด Fallthrough (15:18)** * **การใช้ชนิดข้อมูลอื่นที่ไม่ใช่ Integral/Enum ในคำสั่ง `switch`** -> **เกิด Compile-time error เนื่องจาก Switch รองรับเฉพาะค่าจำนวนเต็มหรือ enum** -> **ใช้โครงสร้าง `if`-`else if` หรือแปลงค่านั้นให้เป็นจำนวนเต็ม/enum ก่อนใช้ Switch (17:00)** * **ชนิดข้อมูลของผลลัพธ์ในตัวดำเนินการ Ternary ขัดแย้งกัน** -> **เกิด Compile-time error เนื่องจากทางเลือกทั้งสองใน Ternary ต้องเป็นประเภทเดียวกัน** -> **แปลงชนิดข้อมูลของทางเลือกทั้งสองให้ตรงกันก่อนเรียกใช้งาน (20:09)** * **ลืมคำนึงถึงการขยายประเภทข้อมูลอัตโนมัติ (Implicit Promotion) ใน Ternary** -> **Compiler จะโปรโมตชนิดข้อมูลที่มีขนาดเล็กกว่า (เช่น `int` ไปเป็น `float`) โดยอัตโนมัติ** -> **ระบุประเภทข้อมูลชัดเจนหรือใช้ `auto` ช่วยตรวจสอบผลลัพธ์การแปลงประเภทข้อมูล (21:00)** * **การเรียกใช้ตัวแปรที่ถูกประกาศไว้ภายในบล็อก `if`/`switch` จากภายนอก** -> **เกิด Compile-time error เนื่องจากตัวแปรจะหมดอายุขัยทันทีที่ออกจากบล็อก** -> **ประกาศตัวแปรไว้ในสโคปที่กว้างขึ้นหากต้องการนำค่านั้นไปใช้ต่อด้านนอก (22:00, 10:11)** * **ประกาศสร้างตัวแปรในเคสหนึ่งแต่ไปเรียกใช้งานในอีกเคสหนึ่งของ Switch** -> **การข้ามการทำงานของ Constructor ทำให้เกิดค่าขยะหรือ Undefined behavior** -> **ใช้เครื่องหมายปีกกาครอบโค้ดแต่ละ Case เพื่อสร้าง Scope เฉพาะ (10:12)** * **ลืมไปว่าการตรวจสอบเงื่อนไขจะหยุดทำงานที่เงื่อนไขแรกที่เป็นจริง** -> **บล็อก `else if` ถัดๆ ไปจะไม่ถูกนำมาคำนวณเลยแม้ว่าเงื่อนไขจะเป็นจริง** -> **เรียงลำดับเงื่อนไขให้ถูกต้อง หรือแยกเขียนเป็นคำสั่ง `if` อิสระต่อกัน (08:40)** * **ใช้ Short-circuit กับฟังก์ชันที่มีผลกระทบข้างเคียง (Side-effect)** -> **ฟังก์ชันในเงื่อนไขหลังๆ จะไม่ถูกเรียกทำงานหากรู้ผลตรรกะตั้งแต่แรกแล้ว** -> **เรียกทำงานฟังก์ชันและเก็บผลลัพธ์ลงตัวแปรก่อนนำไปเปรียบเทียบในเงื่อนไข (13:00)** * **เขียนโค้ดที่ไวยากรณ์ผิดพลาดไว้ในสโคปของ `if constexpr` ที่โดนตัดทิ้ง** -> **เกิด Compile-time error เนื่องจากโค้ดที่ไม่ได้ใช้ยังคงถูกตรวจสอบไวยากรณ์พื้นฐาน** -> **ตรวจสอบให้แน่ใจว่าโค้ดทุกส่วนในฟังก์ชัน Template ถูกต้องตามไวยากรณ์เสมอ (10:08)**

📎 Repo Files

  • 10.FlowControl/10.2IfStatements/main.cpp
  • 10.FlowControl/10.3ElseIf/main.cpp
  • 10.FlowControl/10.4Switch/main.cpp
  • 10.FlowControl/10.5ShortCircuitEvaluation/main.cpp
  • 10.FlowControl/10.6IntegralLogicConditions/main.cpp
  • 10.FlowControl/10.7TernaryOperators/main.cpp
  • 10.FlowControl/10.8IfConstexpr/main.cpp
  • 10.FlowControl/10.9IfWithInitializer/main.cpp
  • 10.FlowControl/10.10SwitchWithInitializer/main.cpp
  • 10.FlowControl/10.11VariableScopeRevisited/main.cpp
  • 10.FlowControl/10.12SwitchScope/main.cpp