Giao diện
🌍 Scope & Lifetime — Phạm vi và Vòng đời
Scope quyết định ở đâu có thể truy cập biến. Lifetime quyết định khi nào biến tồn tại. Hiểu hai khái niệm này tránh được nhiều bugs tinh vi.
Scope vs Lifetime
| Concept | Câu hỏi | Ví dụ |
|---|---|---|
| Scope | Biến visible ở đâu? | Block {}, function, file, global |
| Lifetime | Biến tồn tại bao lâu? | Automatic, static, dynamic |
1. Local Variables (Block Scope)
cpp
#include <iostream>
int main() {
// x visible từ đây
int x = 10;
if (true) {
// y chỉ visible trong block này
int y = 20;
std::cout << "x = " << x << std::endl; // OK
std::cout << "y = " << y << std::endl; // OK
}
// y không còn nữa (destroyed)
std::cout << "x = " << x << std::endl; // OK
// std::cout << "y = " << y << std::endl; // ❌ Error: y không tồn tại
return 0;
}Lifetime của Local Variables
cpp
#include <iostream>
void demonstrate() {
int local = 42; // Created khi function được gọi
std::cout << "local = " << local << std::endl;
} // local bị DESTROYED ở đây (stack unwinding)
int main() {
demonstrate(); // local được tạo
demonstrate(); // local mới được tạo (không liên quan cái cũ)
return 0;
}Stack Frame khi gọi demonstrate():
┌─────────────────────┐
│ local = 42 │ ← Tạo khi function start
├─────────────────────┤
│ return address │
└─────────────────────┘
↓
Khi function return, frame bị pop → local destroyed2. Global Variables (File/Namespace Scope)
cpp
#include <iostream>
// Global variable — visible toàn bộ file
int globalCounter = 0;
void increment() {
++globalCounter; // Access global
}
void decrement() {
--globalCounter; // Access same global
}
int main() {
increment();
increment();
increment();
decrement();
std::cout << "globalCounter = " << globalCounter << std::endl; // 2
return 0;
}Lifetime của Global Variables
cpp
// globals.cpp
#include <iostream>
class Logger {
public:
Logger() {
std::cout << "Logger constructed" << std::endl;
}
~Logger() {
std::cout << "Logger destroyed" << std::endl;
}
};
// Global — Constructed BEFORE main(), destroyed AFTER main()
Logger globalLogger;
int main() {
std::cout << "main() starts" << std::endl;
std::cout << "main() ends" << std::endl;
return 0;
}Output:
Logger constructed ← Before main()
main() starts
main() ends
Logger destroyed ← After main()⚠️ Global Variables: Use with Caution!
| Problem | Explanation |
|---|---|
| Hidden dependencies | Functions secretly depend on global state |
| Thread-unsafe | Multiple threads access → race conditions |
| Testing difficulty | Hard to mock/reset between tests |
| Initialization order | Order of construction across files is undefined! |
Best Practice: Minimize globals. Prefer dependency injection.
3. Static Variables
Static Local Variables — "Nhớ" giá trị giữa các lần gọi
cpp
#include <iostream>
void counter() {
static int count = 0; // Initialized ONCE, tồn tại mãi
++count;
std::cout << "Called " << count << " times" << std::endl;
}
int main() {
counter(); // Called 1 times
counter(); // Called 2 times
counter(); // Called 3 times
return 0;
}Visualization:
Lần gọi 1:
┌─────────────────────┐
│ static count = 0 │ → Initialized
│ count = 1 │ → Incremented
└─────────────────────┘
Lần gọi 2:
┌─────────────────────┐
│ static count = 1 │ → Giữ từ lần trước!
│ count = 2 │ → Incremented
└─────────────────────┘
Lần gọi 3:
┌─────────────────────┐
│ static count = 2 │ → Giữ từ lần trước!
│ count = 3 │ → Incremented
└─────────────────────┘Static trong Class (Shared giữa tất cả objects)
cpp
#include <iostream>
class Counter {
public:
static int totalInstances; // Declaration
Counter() {
++totalInstances;
std::cout << "Counter created. Total: " << totalInstances << std::endl;
}
~Counter() {
--totalInstances;
}
};
// Definition của static member (BẮT BUỘC ở file scope)
int Counter::totalInstances = 0;
int main() {
Counter a; // Total: 1
Counter b; // Total: 2
{
Counter c; // Total: 3
} // c destroyed, Total: 2
std::cout << "Final: " << Counter::totalInstances << std::endl; // 2
return 0;
}static ở File Scope — Internal Linkage
cpp
// file1.cpp
static int hiddenVar = 42; // Chỉ visible trong file1.cpp
// file2.cpp
// extern int hiddenVar; // ❌ Linker error: không tìm thấy!4. extern — External Linkage
cpp
// config.h
#ifndef CONFIG_H
#define CONFIG_H
extern int maxConnections; // Declaration (không allocate memory)
extern const char* appName;
#endif
// config.cpp
#include "config.h"
int maxConnections = 100; // Definition (allocate memory)
const char* appName = "PENALGO";
// main.cpp
#include "config.h"
#include <iostream>
int main() {
std::cout << "App: " << appName << std::endl;
std::cout << "Max: " << maxConnections << std::endl;
return 0;
}5. Shadowing — Ẩn biến outer scope
cpp
#include <iostream>
int x = 100; // Global
int main() {
int x = 50; // Local — SHADOWS global x
std::cout << "Local x: " << x << std::endl; // 50
std::cout << "Global x: " << ::x << std::endl; // 100 (scope resolution)
if (true) {
int x = 10; // Shadows local x của main
std::cout << "Inner x: " << x << std::endl; // 10
}
std::cout << "Local x (unchanged): " << x << std::endl; // 50
return 0;
}⚠️ Tránh Shadowing!
Shadowing gây confusion. Các compilers hiện đại có warning -Wshadow:
bash
g++ -Wshadow main.cpp
# warning: declaration of 'x' shadows a global declarationSummary: Storage Duration Table
| Keyword | Scope | Lifetime | Memory Location |
|---|---|---|---|
| (none) — local | Block | Block | Stack |
static (in function) | Block | Program | Data segment |
static (at file scope) | File | Program | Data segment |
extern | Global | Program | Data segment |
new/delete | N/A | Manual | Heap |
🐛 Bug Hunt Challenge
🐛 Bug Hunt
Đoạn code sau có bug gì?
cpp
#include <iostream>
int* createArray() {
int arr[5] = {1, 2, 3, 4, 5};
return arr;
}
int main() {
int* ptr = createArray();
for (int i = 0; i < 5; ++i) {
std::cout << ptr[i] << " ";
}
std::cout << std::endl;
return 0;
}💡 Gợi ý
arr là local variable với lifetime nào?
🔍 Giải thích & Fix
Bug: arr là local array trên stack. Khi createArray() return, stack frame bị pop → memory của arr không còn hợp lệ!
ptr trở thành dangling pointer → Undefined Behavior.
Fix 1 — Static array:
cpp
int* createArray() {
static int arr[5] = {1, 2, 3, 4, 5};
return arr; // static tồn tại cả chương trình
}Fix 2 — Heap allocation:
cpp
int* createArray() {
int* arr = new int[5]{1, 2, 3, 4, 5};
return arr; // Caller phải delete[]!
}Fix 3 — Modern C++ (std::vector):
cpp
#include <vector>
std::vector<int> createArray() {
return {1, 2, 3, 4, 5}; // Return by value, move semantics
}📚 Tổng kết
| Concept | Key Point |
|---|---|
| Local variables | Scope = block, Lifetime = automatic |
| Global variables | Visible everywhere, use sparingly |
| Static local | Remembers value between calls |
| Static file scope | Internal linkage (file-private) |
| extern | Share variables across files |
| Shadowing | Avoid! Use -Wshadow |
➡️ Tiếp theo
Tiếp theo: const Best Practices — Sử dụng const hiệu quả trong function arguments.