Giao diện
🔧 Functions — Hàm trong C++
Functions là building blocks của mọi chương trình. Hiểu cách truyền tham số quyết định performance và tính đúng đắn của code.
Declaration vs Definition
Declaration (Prototype) — Chỉ khai báo
cpp
// Declaration: Nói "hàm này tồn tại"
// Thường đặt trong header file (.h, .hpp)
int add(int a, int b); // OK
double calculateArea(double radius); // OK
void greet(std::string name); // OK - tên param là optional
void process(int, int); // OK - không cần tên paramDefinition — Cài đặt đầy đủ
cpp
// Definition: Cài đặt logic của hàm
// Thường đặt trong source file (.cpp)
int add(int a, int b) {
return a + b;
}
double calculateArea(double radius) {
return 3.14159 * radius * radius;
}Tại sao tách Declaration và Definition?
┌─────────────────────────────────────────────────────────────────┐
│ math.h (Header) math.cpp (Source) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ // Declaration only #include "math.h" │
│ int add(int a, int b); │
│ int multiply(int a, int b); // Definitions │
│ int add(int a, int b) { │
│ return a + b; │
│ } │
│ │
│ int multiply(int a, int b) { │
│ return a * b; │
│ } │
│ │
└─────────────────────────────────────────────────────────────────┘
▼
main.cpp chỉ cần #include "math.h"
→ Compile nhanh hơn (không cần recompile math.cpp nếu main thay đổi)Pass by Value — Truyền bản sao
cpp
#include <iostream>
void doubleValue(int x) {
x = x * 2; // Chỉ modify bản sao local
std::cout << "Inside function: x = " << x << std::endl;
}
int main() {
int num = 10;
std::cout << "Before: num = " << num << std::endl;
doubleValue(num); // Truyền bản sao của num
std::cout << "After: num = " << num << std::endl;
return 0;
}Output:
Before: num = 10
Inside function: x = 20
After: num = 10 ← num không thay đổi!Ưu/Nhược điểm Pass by Value
| ✅ Ưu điểm | ❌ Nhược điểm |
|---|---|
| An toàn — không thay đổi original | Tốn memory cho copy |
| Dễ hiểu, predictable | Chậm với objects lớn |
| Thread-safe (mỗi thread có bản sao riêng) |
Pass by Reference — Truyền tham chiếu
cpp
#include <iostream>
void doubleValue(int& x) { // Note: int& (reference)
x = x * 2; // Modify trực tiếp biến gốc!
}
int main() {
int num = 10;
std::cout << "Before: num = " << num << std::endl;
doubleValue(num); // Truyền reference
std::cout << "After: num = " << num << std::endl;
return 0;
}Output:
Before: num = 10
After: num = 20 ← num đã thay đổi!Pass by Const Reference — Read-only, không copy
cpp
#include <iostream>
#include <string>
#include <vector>
// ❌ BAD: Copy toàn bộ vector (có thể hàng triệu elements!)
void printBad(std::vector<int> v) {
for (const auto& x : v) {
std::cout << x << " ";
}
}
// ✅ GOOD: Không copy, chỉ reference, không modify
void printGood(const std::vector<int>& v) {
for (const auto& x : v) {
std::cout << x << " ";
}
// v.push_back(100); // ❌ Error: v is const
}
// ✅ GOOD: Với string
void greet(const std::string& name) {
std::cout << "Hello, " << name << "!" << std::endl;
}📌 HPN Standard: Khi nào dùng gì?
| Situation | Syntax | Reason |
|---|---|---|
| Modify original | T& | Thay đổi biến gốc |
| Read large object | const T& | Tránh copy, không modify |
| Small primitives | T (by value) | Copy rẻ, đơn giản |
| Optional/nullable | T* (pointer) | Có thể nullptr |
Golden Rule: Mặc định dùng const T& cho objects. Chỉ dùng by-value cho primitives (int, double, char, bool).
Pass by Pointer vs Reference
cpp
#include <iostream>
// Pass by pointer — Có thể null
void processPtr(int* ptr) {
if (ptr != nullptr) {
*ptr = 100;
}
}
// Pass by reference — Guaranteed non-null
void processRef(int& ref) {
ref = 100;
}
int main() {
int x = 42;
processPtr(&x); // Phải dùng &
std::cout << "After ptr: x = " << x << std::endl;
processRef(x); // Không cần &, tự động bind
std::cout << "After ref: x = " << x << std::endl;
// Pointer có thể null
processPtr(nullptr); // OK, hàm handle được
// Reference KHÔNG thể null
// processRef(nullptr); // ❌ Compiler error
return 0;
}| Aspect | Pointer (T*) | Reference (T&) |
|---|---|---|
| Nullable? | ✅ Có thể nullptr | ❌ Không thể |
| Rebindable? | ✅ ptr = &other; | ❌ Một lần mãi mãi |
| Syntax | *ptr để access | Trực tiếp ref |
| Use case | Optional params | Required params |
Default Arguments
cpp
#include <iostream>
#include <string>
// Default arguments: Từ phải sang trái!
void greet(const std::string& name, const std::string& greeting = "Hello") {
std::cout << greeting << ", " << name << "!" << std::endl;
}
// ❌ Invalid: default argument không ở cuối
// void bad(int a = 10, int b) { ... }
int main() {
greet("Alice"); // Hello, Alice!
greet("Bob", "Xin chào"); // Xin chào, Bob!
return 0;
}Function Overloading
cpp
#include <iostream>
#include <string>
// Cùng tên, khác parameters
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
std::string add(const std::string& a, const std::string& b) {
return a + b;
}
int main() {
std::cout << add(1, 2) << std::endl; // 3 (int version)
std::cout << add(1.5, 2.5) << std::endl; // 4.0 (double version)
std::cout << add("Hello, ", "World!") << std::endl; // Hello, World!
return 0;
}⚠️ Overloading không dựa trên return type
cpp
int getValue();
double getValue(); // ❌ Error: chỉ khác return type
// Compiler không biết gọi cái nào khi:
// auto x = getValue();Return by Value, Reference, and Pointer
cpp
#include <iostream>
#include <vector>
#include <string>
// Return by value — OK cho small objects
int getAnswer() {
return 42;
}
// Return by reference — DANGEROUS nếu return local!
// ❌ BAD: Dangling reference
std::string& badGetName() {
std::string name = "Alice";
return name; // name bị destroy sau khi function ends!
}
// ✅ GOOD: Return reference đến parameter hoặc member
std::string& getLonger(std::string& a, std::string& b) {
return (a.size() > b.size()) ? a : b;
}
// ✅ GOOD: Return by value (Move semantics xử lý efficiency)
std::vector<int> createVector() {
std::vector<int> v = {1, 2, 3, 4, 5};
return v; // Move, không copy (C++11)
}🐛 Bug Hunt Challenge
🐛 Bug Hunt
Đoạn code sau có bug gì?
cpp
#include <iostream>
void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int x = 10, y = 20;
swap(x, y);
std::cout << "x = " << x << ", y = " << y << std::endl;
return 0;
}💡 Gợi ý
Hàm swap nhận parameters như thế nào?
🔍 Giải thích & Fix
Output: x = 10, y = 20 — Không swap!
Bug: Parameters a và b được pass by value → chỉ swap bản copy.
Fix:
cpp
void swap(int& a, int& b) { // Pass by reference
int temp = a;
a = b;
b = temp;
}Hoặc dùng std::swap:
cpp
#include <utility>
std::swap(x, y);📚 Tổng kết
| Concept | Syntax | Use Case |
|---|---|---|
| Pass by value | func(T x) | Small types, cần copy |
| Pass by reference | func(T& x) | Modify original |
| Pass by const ref | func(const T& x) | Read-only, avoid copy |
| Pass by pointer | func(T* x) | Nullable, optional |
| Default arguments | func(int x = 10) | Optional params |
| Overloading | Same name, diff params | Polymorphism |
➡️ Tiếp theo
Tiếp theo: Scope & Lifetime — Local, Global, và Static variables.