Tổng Hợp Kiến Thức C++

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:

  1. Viết code trong editor, lưu thành file .cpp
  2. Biên dịch: g++ tenfile.cpp -o a.out
  3. Chạy: ./a.out (Linux) hoặc a.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ểuKích thước
bool1 byte
char1 byte
int4 bytes
float4 bytes
double8 bytes
long double16 bytes
long long8 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ể

Thẻ: C++ Template STL oop pointer

Đăng vào ngày 17 tháng 7 lúc 23:00