Mục lục
Khái niệm cơ bản, Từ khóa, Kiểu dữ liệu, Container, Toán tử, Con trỏ, Hàm, Lớp, Template, Quản lý bộ nhớ, Thư viện liên kết, Tiêu chuẩn code
Khái niệm cơ bản
Giới thiệu
C++ là ngôn ngữ lập trình đa mẫu hình (multi-paradigm) với đặc điểm: kiểu tĩnh (static typing), biên dịch (compiled), đa năng (general-purpose), phân biệt hoa thường (case-sensitive), và cú pháp phức tạp. Nó kết hợp cả ngôn ngữ bậc cao và bậc thấp, hỗ trợ lập trình thủ tục (procedural), lập trình hướng đối tượng (OOP), và lập trình tổng quát (generic programming).
Tiêu chuẩn C++ gồm ba thành phần chính:
- Core language: cung cấp các khối xây dựng cơ bản như biến, kiểu dữ liệu, hằng số...
- C++ Standard Library: cung cấp nhiều hàm để thao tác với file, chuỗi...
- Standard Template Library (STL): cung cấp các thuật toán và cấu trúc dữ liệu
Sử dụng cơ bản
Chương trình C++ là tập hợp các đối tượng tương tác qua các phương thức. Một số khái niệm cơ bản:
- Đối tượng (Object): có trạng thái và hành vi. VD: con chó có màu lông, tên, giống (trạng thái), sủa, chạy (hành vi). Đối tượng là thể hiện của lớp.
- Lớp (Class): khuôn mẫu mô tả hành vi/trạng thái của đối tượng.
- Phương thức (Method): biểu diễn hành vi, chứa logic, thao tác dữ liệu.
- Biến cục bộ (Instance variable): mỗi đối tượng có biến riêng, tạo nên trạng thái.
Cách chạy chương trình:
- Viết code trong editor, lưu thành file .cpp
- Biên dịch:
g++ tenfile.cpp -o a.out - Chạy:
./a.out(Linux) hoặca.exe(Windows)
Quá trình biên dịch
Preprocessor → Compile → Assemble → Link
- Preprocessor: xử lý chỉ thị tiền biên dịch (#define, #include...)
- Compile: chuyển mã nguồn thành ngôn ngữ assembly
- Link: liên kết các file object, giải quyết phụ thuộc, tạo file thực thi
Từ khóa và định danh
Định danh (Identifier)
Dùng để đặt tên cho biến, hàm, lớp, module... Bắt đầu bằng chữ cái (A-Z, a-z) hoặc dấu gạch dưới (_), theo sau là chữ cái, số (0-9). Phân biệt hoa thường.
Từ khóa quan trọng
using
// Import namespace
using namespace std;
// Đặt bí danh cho kiểu
using SoNguyen = int;
// Trong lớp kế thừa, truy cập thành viên lớp cha
class Cha {
public:
void hienThi();
};
class Con : public Cha {
public:
using Cha::hienThi;
};
#include
Là chỉ thị tiền biên dịch, sao chép toàn bộ nội dung file vào vị trí include.
Nguyên tắc: C++ cho phép khai báo nhiều lần nhưng chỉ cho phép định nghĩa một lần. Nếu nhiều file .cpp cùng include một file .h chứa định nghĩa hàm, sẽ gây lỗi LNK1169 (tìm thấy ký hiệu được định nghĩa nhiều lần).
Giải pháp đúng: khai báo hàm trong .h, định nghĩa trong một file .cpp riêng.
#define
Là lệnh tiền biên dịch, thay thế văn bản đơn giản trong giai đoạn tiền biên dịch. Nên dùng const thay cho #define.
// 1. Định nghĩa macro thay thế
#define INRA_CHUOI printf
// 2. Macro có điều kiện
#ifdef DEBUG_MODE
#define LOG(msg) printf(msg)
#else
#define LOG(msg)
#endif
// 3. Macro với tham số
#define BINH_PHUONG(x) ((x) * (x))
// 4. Stringification và concatenation
#define TO_CHUOI(x) #x // Chuyển thành chuỗi
#define NOI(x, y) x##y // Nối hai token
// 5. Variadic macro
#define IN_RA(fmt, ...) printf(fmt, ##__VA_ARGS__)
// 6. Macro debug
#ifdef _DEBUG
#define TRACEDBG(fmt, ...) \
printf("%s %s (%d): ", __FILE__, __PRETTY_FUNCTION__, __LINE__); \
printf(fmt, ##__VA_ARGS__)
#else
#define TRACEDBG(fmt, ...)
#endif
// Các macro có sẵn của compiler:
// __FILE__, __LINE__, __FUNCTION__, __DATE__, __TIME__
typedef
Xử lý khi biên dịch, đặt bí danh cho kiểu đã tồn tại.
typedef int* ConTroSoNguyen;
typedef char MangKyTu[100];
// So sách:
ConTroSoNguyen p1, p2; // tương đương: int *p1, *p2;
#define ConTroSoNguyen2 int*
ConTroSoNguyen2 p3, p4; // tương đương: int *p3, p4; (sai!)
const
Ràng buộc đối tượng không thay đổi, do compiler thực thi. Có thể dùng với mảng, con trỏ, thành viên lớp.
So sánh const với #define:
- #define: tiền biên dịch, không có kiểu, không an toàn
- const: biên dịch, có kiểu, an toàn, #undef để hủy
- #define lưu trong code segment, không cấp phát bộ nhớ
- const lưu trong data segment, cấp phát bộ nhớ
extern const int HangSo; // Tăng phạm vi với extern
const int* p1; // Con trỏ hằng (không đổi được giá trị pointed)
int* const p2; // Con trỏ hằng (không đổi được con trỏ)
const int* const p3;// Cả hai đều hằng
constexpr
Tối ưu hóa tại thời điểm biên dịch, tăng hiệu suất. Có thể dùng với hàm và constructor.
constexpr int BAC_2 = 2;
constexpr int TONG = 1 + 2 + 3;
// Hàm constexpr với đệ quy
constexpr int GIAI_THUA(int n) {
return n == 0 ? 1 : n * GIAI_THUA(n - 1);
}
// Fibonacci tại thời điểm biên dịch
constexpr int FIBO(int n) {
return n <= 2 ? 1 : FIBO(n-1) + FIBO(n-2);
}
// Dùng: khai báo mảng tĩnh
int mang[FIBO(10)]; // Tính tại compile-time
static
Thay đổi vùng nhớ, thời gian sống, phạm vi.
Trong hàm (C-style):
- Biến cục bộ: lưu trong static area, sống suốt chương trình, chia sẻ trong file
- Hàm: chỉ sử dụng trong file khai báo, tránh xung đột tên
Trong lớp:
- Thành viên dữ liệu: thuộc về lớp, không thuộc đối tượng, truy cập không cần tạo object
- Thành viên hàm: gọi được không cần object, dùng TenLop::TenHam(), không truy cập non-static
inline
Nhúng code trực tiếp vào nơi gọi, tăng hiệu suất nhưng tốn bộ nhớ. Compiler quyết định có inline hay không. Hàm ảo có thể inline nếu không đa hình.
extern
Khai báo tham chiếu (reference declaration), biến được định nghĩa nơi khác.
extern int BienChung; // Chỉ khai báo, không định nghĩa
int BienChinh; // Vừa khai báo vừa định nghĩa
register
Yêu cầu lưu trong register, nhưng compiler tự quyết định. Với 32-bit compiler không hiệu quả.
auto
Compiler tự suy luận kiểu. Dùng cho biến, kiểu trả về hàm, tham số (C++20).
auto x = 5; // int
auto y = 3.14; // double
auto z = "hello"; // const char*
// Range-based for
vector<int> ds = {1, 2, 3, 4};
for (auto &pt : ds) pt += 1; // Có thể sửa
for (auto pt : ds) cout << pt; // Chỉ đọc
// C++20: tham số hàm
auto TinhTong(auto a, auto b) { return a + b; }
explicit
Ngăn chặn chuyển đổi ngầm định. Chỉ dùng với constructor có một tham số.
new và malloc
Đều cấp phát bộ nhớ:
- malloc: C library, cấp phát trên heap, trả về void*
- new: C++ operator, cấp phát trên free store, gọi constructor
- new thất bại ném exception, malloc trả về NULL
Kiểu dữ liệu
Kiểu cơ bản
| Kiểu | Kích thước |
| bool | 1 byte |
| char | 1 byte |
| int | 4 bytes |
| float | 4 bytes |
| double | 8 bytes |
| long double | 16 bytes |
| long long | 8 bytes |
Lưu ý:
- Số octal có tiền tố 0, hex có tiền tố 0x
- Không có ký hiệu số nhị phân trực tiếp
- Nên dùng long long để tránh overflow
Con trỏ
Con trỏ 32-bit: 4 bytes; 64-bit: 8 bytes. Kích thước con trỏ = kích thước địa chỉ bộ nhớ.
char* mang[10]; // Mảng 10 phần tử, mỗi phần tử là char*
char (*conTro)[10]; // Con trỏ đến mảng 10 char
Type Deduction
auto và decltype
auto x = 10; // int
auto y = x + 5; // int
// decltype: suy luận kiểu từ biểu thức
int a = 1, b = 2;
decltype(a + b) c; // int c
// Tail return type (C++11)
auto Cong(int x, int y) -> decltype(x + y) {
return x + y;
}
// C++14: không cần ->decltype
auto Nhan(int x, int y) { return x * y; }
// decltype(auto)
int bien = 5;
decltype(auto) x1 = bien; // int (copy)
decltype(auto) x2 = (bien); // int& (reference)
register
Chỉ cho biến cục bộ, không cho toàn cục hoặc hàm. Phải là biến đơn, kích thước nhỏ.
Chuyển đổi kiểu
static_cast
Chuyển đổi có định nghĩa rõ, không đụng đến const.
int a = 10;
double b = static_cast<double>(a);
// Upcast: an toàn
Derived* d = new Derived();
Base* b = static_cast (d);
// Downcast: không kiểm tra, nguy hiểm
Base* b2 = new Base();
Derived* d2 = static_cast(b2); // Không an toàn!
dynamic_cast
Chuyển đổi lên/xuống với RTTI, yêu cầu lớp có hàm ảo.
Base* b = new Derived();
Derived* d = dynamic_cast(b); // OK, nếu b thực sự là Derived
if (d) { /* sử dụng d */ }
// Thất bại với pointer: trả về nullptr
// Thất bại với reference: ném bad_cast
const_cast
Thay đổi const/volatile.
const int x = 10;
int* p = const_cast(&x);
*p = 20; // Hành vi không xác định!
reinterpret_cast
Chuyển đổi kiểu bit-level, nguy hiểm.
Boost conversion library
#include "boost/cast.hpp"
// polymorphic_cast: tương tự dynamic_cast
Base* b = new Derived();
Derived* d = boost::polymorphic_cast(b);
// polymorphic_downcast: dùng trong debug, static_cast trong release
Base* b2 = new Derived();
Derived* d2 = boost::polymorphic_downcast(b2);
// numeric_cast: kiểm tra range
try {
int x = boost::numeric_cast<int>(bigNumber);
} catch (boost::bad_numeric_cast& e) {}
// lexical_cast: chuyển đổi chuỗi
string s = boost::lexical_cast<string>(123);
int n = boost::lexical_cast<int>("456");
Initialization
vector<int> ds = {1, 2, 3, 4};
if (auto it = find(ds.begin(), ds.end(), 3); it != ds.end())
*it = 4;
Right-value Reference
Khái niệm
- Lvalue: có địa chỉ, tồn tại sau phép gán
- Rvalue: không thể gán, tạm thời
- 左值引用: alias thông thường
- 右值引用: bind đến tạm, không đặt tên được
Move semantics
string s1 = "Hello";
string s2 = s1; // Copy, tốn thời gian
string s3 = move(s1); // Move, s1 trở nên rỗng
// Di chuyển constructor
class Buffer {
public:
Buffer(Buffer&& other) noexcept {
data = other.data;
size = other.size;
other.data = nullptr;
}
private:
char* data;
int size;
};
Perfect forwarding
void XuLy(T&& v) {
Ham(std::forward<T>(v)); // Giữ nguyên loại tham chiếu
}
Nhập xuất
#include <iostream>
#include <string>
// getline
string line;
getline(cin, line);
// Đọc đến ký tự
cin.getline(buffer, size, delimiter);
Container
Các loại Container
- Sequence: vector, deque, list, forward_list
- Associative: set, map, multiset, multimap
- Unordered: unordered_set, unordered_map...
- Adapter: stack, queue, priority_queue
Vector
#include <vector>
#include <algorithm>
#include <numeric>
vector<int> v(10, 0); // 10 phần tử, giá trị 0
vector mt(5, vector<int>(3, 0)); // Ma trận 5x3
// Thao tác
v.push_back(5);
v.pop_back();
v.front(); v.back();
v.begin(); v.end();
v.empty(); v.clear();
v.size(); v.capacity();
v.reserve(100); // Dự trữ, không thay đổi size
v.resize(50); // Thay đổi cả capacity và size
v.shrink_to_fit();
// Chèn, xóa
v.insert(v.begin() + 2, 10);
v.erase(v.begin(), v.begin() + 5);
// Hoán đổi
v.swap(v1);
// Thuật toán
reverse(v.begin(), v.end());
sort(v.begin(), v.end());
sort(v.begin(), v.end(), [](int a, int b){ return a > b; });
*min_element(v.begin(), v.end());
*max_element(v.begin(), v.end());
// Tìm kiếm
auto it = find(v.begin(), v.end(), 5);
Deque
Giống vector nhưng thao tác ở đầu/cuối hiệu quả O(1).
deque<int> d;
d.push_front(1);
d.push_back(2);
d.pop_front();
d.pop_back();
d.front(); d.back();
List
Danh sách liên kết đôi, chèn/xóa O(1) ở giữa.
#include <list>
list<int> lst;
lst.push_back(1);
lst.push_front(2);
lst.remove(5); // Xóa tất cả phần tử = 5
lst.unique(); // Xóa trùng lặp
lst.sort();
lst.merge(lst2); // Hợp nhất đã sắp xếp
lst.splice(it, lst2, first, last); // Di chuyển phần tử
String
string s("Hello");
string s2(s);
string s3(s, 1, 3); // "ell"
string s4(5, 'a'); // "aaaaa"
// Thao tác
s += " World";
s.append("!");
s.push_back('x');
s.pop_back();
s.erase(0, 3);
s.substr(0, 5);
s.find("lo");
s.rfind("lo");
s.replace(0, 3, "He");
s.compare(s2);
s.c_str(); // C-string
// Tìm kiếm
size_t pos = s.find('@');
if (pos != string::npos) { }
Stack và Queue
stack<int> st;
st.push(1);
st.top();
st.pop();
st.empty(); st.size();
queue<int> q;
q.push(1);
q.front(); q.back();
q.pop();
Tuple
#include <tuple>
tuple t(1, "abc", 3.14);
auto t2 = make_tuple(2, "def", 2.71);
// Truy cập
get<0>(t);
get<int>(t);
// Số lượng
tuple_size::value;
// Nối tuple
auto t3 = tuple_cat(t, t2);
// Giải nén (unpack)
int a; string b; double c;
tie(a, b, c) = t;
// Return nhiều giá trị
tuple TraVe() {
return make_tuple(1, 2.5, "ok");
}
// C++17 structured binding
auto [x, y, z] = TraVe();
Map
#include <map>
map m;
m["mot"] = 1;
m.insert({"hai", 2});
m.insert(pair("ba", 3));
// Tìm kiếm
auto it = m.find("mot");
if (it != m.end())
cout << it->second;
// Duyệt
for (auto& [key, value] : m)
cout << key << ": " << value;
// Xóa
m.erase("mot");
m.erase(it);
Unordered Map
#include
unordered_map um = {{1, "one"}, {2, "two"}};
// Nhanh hơn map trong tìm kiếm O(1)
// Không sắp xếp
Set
#include <set>
set<int> s;
s.insert(5);
s.insert(3);
s.insert(7); // Tự động sắp xếp
// Không cho phép trùng lặp
// Multiset cho phép
multiset<int> ms;
ms.insert(5);
ms.insert(5); // OK
Array
#include <array>
array a = {1, 2, 3, 4, 5};
a.data(); // Con trỏ đến dữ liệu
a.fill(0); // Điền giá trị
a.swap(a2); // Hoán đổi
Enum
// C++11 enum thường (yếu)
enum Mau { Do, Xanh, Tim };
enum class MauMoi : int { Do, Xanh, Tim };
// Dùng
Mau m = Do;
MauMoi mm = MauMoi::Do;
// Chỉ định giá trị
enum class HangMuc : unsigned char {
GiaTri1 = 1,
GiaTri2,
GiaTri3 = 10
};
Variant (C++17)
#include <variant>
variant v = 5;
// Kiểm tra
cout << v.index(); // 0 (int)
v = 3.14;
cout << v.index(); // 1 (double)
// Truy cập an toàn
if (auto p = get_if<int>(&v))
cout << *p;
// Truy cập không an toàn
try {
int x = get<int>(v); // Ném bad_variant_access
} catch (exception& e) {}
// Visit
visit([](auto&& arg) { cout << arg; }, v);
Toán tử
- Số học: + - * / % ++ --
- So sánh: == != > < >= <=
- Logic: && || !
- Bit: & | ^ ~ << >>
- Gán: = += -= *= /= %= &= |= ^= <<= >>=
- Khác: ?: (conditional), sizeof, . ->, cast, & *, typeid
Operator Overloading
Không thể overload: :: . .* ?: sizeof typeid
class PhanSo {
public:
int tu, mau;
PhanSo operator+(const PhanSo& other) const {
return {tu * other.mau + other.tu * mau, mau * other.mau};
}
friend ostream& operator<<(ostream& os, const PhanSo& ps) {
return os << ps.tu << "/" << ps.mau;
}
};
Con trỏ
Con trỏ và Tham chiếu
int a = 10;
int* p = &a; // Con trỏ
int& r = a; // Tham chiếu, alias
// Khác biệt:
// - Con trỏ có bộ nhớ riêng, tham chiếu không
// - sizeof(con trỏ) = 4 hoặc 8 bytes
// - Con trỏ có thể NULL, tham chiếu phải gán
// - Con trỏ có thể đổi pointed, tham chiếu không
// - Con trỏ có cấp độ, tham chiếu chỉ một cấp
Con trỏ Null và Con trỏ Wild
int* p = nullptr; // Con trỏ null (C++11)
int* q; // Con trỏ wild - CHƯA KHỞI TẠO!
// Tránh wild pointer:
// - Luôn khởi tạo
// - Xóa sau khi delete (gán nullptr)
// - Không trả về địa chỉ cục bộ
Smart Pointer
#include <memory>
// unique_ptr: độc quyền sở hữu
auto up = make_unique<int>(10);
unique_ptr<DuLieu> data = make_unique<DuLieu>();
// Không copy được
// unique_ptr<DuLieu> copy = data; // LỖI
// Phải move
unique_ptr<DuLieu> data2 = std::move(data);
// shared_ptr: chia sẻ sở hữu, dùng reference count
auto sp = make_shared<int>(20);
auto sp2 = sp; // Tăng count
cout << sp.use_count(); // 2
sp.reset(); // Giảm count
// weak_ptr: tham chiếu yếu, không tăng count
weak_ptr<int> wp = sp;
if (auto spt = wp.lock()) // Chuyển thành shared_ptr
cout << *spt;
if (wp.expired()) {} // Đã bị xóa
Con trỏ hàm
void (*Ham)(int) = [](int x) { cout << x; };
Ham(5);
// std::function
function cong = [](int a, int b) { return a + b; };
cout << cong(3, 4);
Hàm
Tham chiếu và Con trỏ trong tham số
Tham chiếu an toàn và nhanh hơn con trỏ. Compiler tối ưu tốt hơn.
// Con trỏ: phải kiểm tra null
void XuLyConTro(int* p) {
if (p) *p = 10;
}
// Tham chiếu: đảm bảo không null
void XuLyThamChieu(int& r) {
r = 10;
}
Functor
Class overload operator()
template<typename T>
struct SoSanh {
bool operator()(const T& a, const T& b) const {
return a > b;
}
};
SoSanh<int> ss;
bool ketQua = ss(5, 3); // true
Lambda
// Cú pháp: [capture](params) -> ret { body }
auto f = [](int x) -> int { return x * 2; };
auto cong = [](auto a, auto b) { return a + b; };
// Capture
int a = 5;
auto lam1 = [a](int x) { return x + a; }; // Copy
auto lam2 = [&a](int x) { a = 10; return x; }; // Reference
auto lam3 = [=]() { return a; }; // Tất cả copy
auto lam4 = [&]() { a = 20; }; // Tất cả ref
auto lam5 = [=, &a]() {}; // Hỗn hợp
// Mutable
auto lam6 = [a]() mutable { a = 10; return a; };
// Init capture (C++14)
auto ptr = make_unique<int>(5);
auto lam7 = [p = move(ptr)]() { return *p; };
// Generic lambda (C++14)
auto Tong = [](auto a, auto b) { return a + b; };
Srand và Random
#include <cstdlib>
#include <ctime>
srand(time(nullptr)); // Khởi tạo seed
int soNgauNhien = rand() % 100; // 0-99
// C++11: tốt hơn
#include <random>
random_device rd;
mt19937 gen(rd());
uniform_int_distribution<> dis(1, 100);
int x = dis(gen); // 1-100
Function Binding
using namespace std::placeholders;
int LonNhat(int a, int b, int c) { return max({a, b, c}); }
auto fn = bind(LonNhat, _1, 5, 10); // a = tham số
cout << fn(3); // = LonNhat(3, 5, 10)
Lớp (Class)
Hàm ảo và V-Table
Chỉ hàm thành viên lớp mới có thể là ảo. Constructor không thể là ảo.
Mỗi lớp có virtual function table (vtable), chứa con trỏ đến hàm ảo. Object có vptr trỏ đến vtable.
class DongVat {
public:
virtual void TiengKeu() = 0; // Pure virtual
virtual void An() {}
void Chay() {} // Non-virtual
};
class Cho : public DongVat {
public:
void TiengKeu() override { cout << " Gau! "; }
void An() override { cout << " Xương "; }
};
Đóng gói, Kế thừa, Đa hình
// Đóng gói: Ẩn chi tiết
class Nguoi {
private:
string ten;
protected:
int tuoi;
public:
void HienThi() const;
};
// Kế thừa
class SinhVien : public Nguoi {
private:
string msv;
public:
void HienThi() const override;
};
// Đa hình: gọi qua con trỏ
Nguoi* p = new SinhVien();
p->HienThi(); // Gọi SinhVien::HienThi()
Kế thừa và Composition
- Is-a: kế thừa
- Has-a: composition
// Composition: A has-a B
class DongMay {
private:
MayChieu mc; // Không phải kế thừa
};
Template Method và Override
// Overload: cùng tên, tham số khác
void In(int x) { cout << x; }
void In(double x) { cout << x; }
// Override: ghi đè hàm ảo
class Cha {
virtual void Lam() {}
};
class Con : public Cha {
void Lam() override {}
};
// Redefine: ẩn hàm cha
class Cha2 {
void Lam(int x) {}
};
class Con2 : public Cha2 {
void Lam() {} // Ẩn Lam(int)
};
Abstract Class
class Hinh {
public:
virtual double TinhDienTich() const = 0; // Pure virtual
virtual ~Hinh() {}
};
Friend
class A {
friend void HienThi(const A&);
friend class B;
};
void HienThi(const A& a) { /* truy cập private */ }
Struct và Class
Struct: mặc định public, dùng cho POCO.
Class: mặc định private, dùng cho OOP.
Union
union DuLieu {
int i;
double d;
char c;
};
Khởi tạo lớp
Delegating Constructor
class Nguoi {
public:
string ten;
int tuoi;
Nguoi() : Nguoi("") {}
Nguoi(string t) : ten(t), tuoi(0) {}
};
Using Constructor (C++11)
class Con : public Cha {
public:
using Cha::Cha; // Kế thừa constructor
Con(int x, int y) : Cha(x), tuoi(y) {}
};
Override và Final
class Cha {
virtual void Lam() final {}
};
class Con : public Cha {
// void Lam() override {} // LỖI vì final
};
class MayTinh final {}; // Không cho phép kế thừa
// class Laptop : public MayTinh {} // LỖI
Default và Delete
class A {
public:
A() = default;
A(const A&) = delete;
A& operator=(const A&) = delete;
};
Constructor và Destructor
Constructor không phải ảo vì object chưa tạo, không biết kiểu thực.
Destructor nên là ảo khi dùng với con trỏ base trỏ đến derived.
class Base {
public:
virtual ~Base() = default;
};
Thứ tự constructor: static khác → base → member → chính nó
Thứ tự destructor: ngược lại
Static Member
class Lop {
public:
static int BienStatic; // Khai báo
static const int HANG = 10;
static void HamStatic() {}
};
int Lop::BienStatic = 0; // Định nghĩa
Template
Class Template
template<typename T>
class Mang {
T* data;
int kichThuoc;
public:
Mang(int n) : kichThuoc(n), data(new T[n]) {}
~Mang() { delete[] data; }
T& operator[](int i) { return data[i]; }
};
Mang<int> m(10); // Phải chỉ định kiểu
Function Template
template<typename T>
T Max(T a, T b) {
return a > b ? a : b;
}
// Gọi
Max<int>(3, 4); // Explicit
Max(3, 4); // Implicit, compiler tự suy
// Hàm thường được ưu tiên
void In(int x) { cout << x; }
template<typename T>
void In(T x) { cout << x; }
In(5); // Gọi hàm thường
Variadic Template
// Cách 1: Đệ quy
void In() {}
template<typename T, typename... Args>
void In(T first, Args... rest) {
cout << first << " ";
In(rest...);
}
// Cách 2: Fold expression (C++17)
template
void In(Args... args) {
((cout << args << " "), ...);
cout << endl;
}
Non-type Template Parameter
template<int N>
int TinhGiaiThua() {
if constexpr (N == 0) return 1;
else return N * TinhGiaiThua();
}
int x = TinhGiaiThua<5>(); // Tính tại compile time
Template Alias
template<typename T>
using Ptr = unique_ptr<T>;
Ptr<int> p = make_unique<int>(5);
External Template (C++11)
template void Ham<int>(int); // Force instantiation
extern template void Ham<int>(int); // Prevent re-instantiation
Quản lý bộ nhớ
Các vùng nhớ
- Stack: biến cục bộ, tham số hàm
- Heap: new/delete, malloc/free
- Data/Static: biến toàn cục, static
- Constant: hằng số
- Code: mã máy
So sách Stack và Heap
- Stack: cấp phát nhanh, quản lý tự động, không phân mảnh
- Heap: cấp phát thủ công, chậm hơn, có thể phân mảnh
Memory Leak
Không giải phóng bộ nhớ đã cấp phát. Sử dụng smart pointer để tránh.
Segfault
Truy cập vùng nhớ không thuộc về process.
Xử lý ngoại lệ
try {
// Code có thể ném ngoại lệ
if (loi) throw MyException("Lỗi!");
} catch (const MyException& e) {
cout << e.what();
} catch (...) {
// Bắt mọi ngoại lệ
}
Lưu ý
- Nên dùng exception thay vì return code
- Khi dùng smart pointer, exception sẽ tự động giải phóng
- Không ném exception trong destructor
Thư viện liên kết
Static Library (.lib/.a)
Code được nhúng vào executable khi biên dịch. Kích thước file lớn hơn, không cần file bổ sung khi chạy.
// Cách 1: Visual Studio
#pragma comment(lib, "mylib.lib")
// Cách 2: CMake
target_link_libraries(myapp mylib)
Dynamic Library (.dll/.so)
Load khi chạy. File nhỏ, cần có dll cùng thư mục.
// Implicit loading
#include "mylib.h"
#pragma comment(lib, "mylib.lib")
void HamSuDung() { MyFunc(); }
// Explicit loading
#include
typedef void (*FUNC)();
HMODULE h = LoadLibrary("mylib.dll");
FUNC f = (FUNC)GetProcAddress(h, "MyFunc");
f();
FreeLibrary(h);
Tiêu chuẩn code
Quy tắc đặt tên
- Files: lowercase, có thể dùng _ hoặc -
- Types: PascalCase: MyClass
- Variables: snake_case: bien_di, member cuối _: bien_
- Constants: kConstantName
- Functions: PascalCase: HamChinh()
- Macros: all caps: MAX_SIZE
- Namespaces: lowercase
Header
- Thứ tự include: thư viện hệ thống C → C++ → thư viện bên thứ 3 → header nội bộ
- Dùng #ifndef hoặc #pragma once tránh double include
- Tham số input trước output
Class
- Thứ tự: public → protected → private
- Trong mỗi phạm vi: typedef → hằng → constructor → destructor → methods → data
- Không kế thừa nhiều, dùng composition thay nếu có thể
- Constructor chỉ khởi tạo cần thiết, dùng Init()
Khác
- Tham số hàm: dùng const reference
- Không dùng default arguments
- Hạn chế dùng exception (hoặc không dùng)
- Ưu tiên dùng stream cho log, printf cho output
- Dùng const mọi nơi có thể