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.cpp10.FlowControl/10.3ElseIf/main.cpp10.FlowControl/10.4Switch/main.cpp10.FlowControl/10.5ShortCircuitEvaluation/main.cpp10.FlowControl/10.6IntegralLogicConditions/main.cpp10.FlowControl/10.7TernaryOperators/main.cpp10.FlowControl/10.8IfConstexpr/main.cpp10.FlowControl/10.9IfWithInitializer/main.cpp10.FlowControl/10.10SwitchWithInitializer/main.cpp10.FlowControl/10.11VariableScopeRevisited/main.cpp10.FlowControl/10.12SwitchScope/main.cpp