1. OOP: Kế thừa, đa hình, đóng gói, trừu tượng
- Kế thừa (Inheritance): Cho phép class con sử dụng lại thuộc tính và phương thức của class cha.
- Đa hình (Polymorphism): Cho phép gọi đúng hàm override dựa trên kiểu runtime thông qua con trỏ hoặc tham chiếu.
- Đóng gói (Encapsulation): Gom toàn bộ thuộc tính và hành vi liên quan vào trong 1 class, và che giấu các chi tiết không cần thiết bằng cách dùng access modifier (private/protected/public).
- Trừu tượng (Abstraction): Tách phần giao diện ra khỏi phần hiện thực, giúp ẩn các chi tiết phức tạp, chỉ hiển thị những gì cần thiết để sử dụng.
class Animal {
public:
virtual void speak() = 0; // Pure virtual function: interface abstraction
};
class Dog : public Animal {
public:
void speak() override { std::cout << "Woof"; }
};
2. Rule of Three / Five / Zero & Constructor Enhancements
Rule of Three: Nếu bạn viết constructor sao chép (copy constructor), bạn nên viết thêm destructor và toán tử gán (copy assignment operator).
Rule of Five: Bổ sung thêm move constructor và move assignment operator với C++11.
Rule of Zero: Sử dụng smart pointer hoặc container để không cần viết bất kỳ hàm quản lý tài nguyên nào.
class Resource {
int* data;
public:
Resource(int v) : data(new int(v)) {}
~Resource() { delete data; }
Resource(const Resource& r) : data(new int(*r.data)) {}
Resource& operator=(const Resource& r) {
if (this != &r) {
delete data;
data = new int(*r.data);
}
return *this;
}
};
Constructor Delegation
Dùng một constructor gọi sang constructor khác để giảm lặp code.
class MyClass {
int x, y;
public:
MyClass(int val) : MyClass(val, 0) {} // gọi constructor 2 tham số
MyClass(int a, int b) : x(a), y(b) {}
};
=default và =delete
-
: Yêu cầu compiler tự sinh hàm constructor hoặc operator mặc định.=default -
: Cấm compiler tự sinh hoặc gọi một hàm.=delete
class Example {
public:
Example() = default; // constructor mặc định
Example(const Example&) = delete; // không cho phép copy
Example& operator=(const Example&) = delete; // không gán được
~Example() = default; // hủy mặc định
};
=delete giúp ngăn lỗi lập trình khi cố gắng copy object không nên
copy. Sử dụng
=default giúp đơn giản code, nhưng cũng nên rõ ràng về ý định thiết kế.
3. Inheritance & Multiple Inheritance: Diamond Problem
Khi một class kế thừa từ nhiều class, có thể gây ra xung đột, đặc biệt khi cùng kế thừa từ một base class — gọi là Diamond Problem.
class A { public: void hello() {} };
class B : public virtual A {};
class C : public virtual A {};
class D : public B, public C {};
D d; d.hello();
virtual khi kế thừa để đảm bảo chỉ tồn tại một instance của A trong D.
Đa hình tĩnh vs động
- Đa hình tĩnh (Compile-time): Xảy ra khi nạp chồng hàm (function overloading) hoặc nạp chồng toán tử (operator overloading). Quyết định tại thời điểm biên dịch.
- Đa hình động (Runtime): Xảy ra khi sử dụng con trỏ hoặc tham chiếu đến lớp cơ sở và override virtual function ở lớp con. Quyết định tại runtime thông qua cơ chế vtable.
// Compile-time polymorphism
int add(int a, int b); // overloading
float add(float a, float b);
// Runtime polymorphism
class Base { public: virtual void show() { std::cout << "Base"; } };
class Derived : public Base { public: void show() override { std::cout << "Derived"; } };
Base* b = new Derived();
b->show(); // prints "Derived"
4. Static Members & Friend Functions
Static member: Dùng chung cho mọi object, không phụ thuộc instance cụ thể.
Lưu
ý: Chỉ có các
static function mới có thể truy cập trực tiếp
static member.
class Counter {
static int count; // chỉ có một biến duy nhất trong bộ nhớ
public:
Counter() { ++count; }
static int getCount() { return count; } // chỉ static function gọi được static member
};
int Counter::count = 0;
- Static function không thể truy cập non-static member vì nó không có đối tượng cụ thể (this pointer).
- Thường dùng để đếm số lượng instance hoặc chia sẻ trạng thái toàn cục giữa các object.
Friend function / friend class: Cho phép truy cập vào thành phần private/protected của class mà không cần kế thừa.
class Box {
private:
int value;
friend void showValue(const Box&);
};
void showValue(const Box& b) {
std::cout << b.value;
}
5. Virtual Destructor & Object Slicing
Virtual destructor: Đảm bảo gọi đúng destructor khi xóa object qua con trỏ base class.
class Base {
public:
virtual ~Base() { std::cout << "Base destructor\n"; }
};
class Derived : public Base {
public:
~Derived() { std::cout << "Derived destructor\n"; }
};
Base* obj = new Derived();
delete obj; // gọi đúng cả 2 destructor
Object Slicing: Khi truyền object bằng giá trị, phần dữ liệu riêng của class con sẽ bị cắt bỏ.
class A {
public: int x;
};
class B : public A {
public: int y;
};
A a = B(); // slicing: y bị bỏ
6. Design Patterns (Singleton, Factory, Strategy, Observer)
Singleton
Đảm bảo chỉ có một instance toàn cục duy nhất và truy cập thống nhất.
class Singleton {
public:
static Singleton& getInstance() {
static Singleton instance; // thread-safe từ C++11
return instance;
}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
private:
Singleton() = default;
};
❌ Nhược điểm: Khó test unit, dễ gây phụ thuộc toàn cục (global dependency).
Factory
Ẩn logic khởi tạo object, tạo object thông qua interface thay vì new trực tiếp.
class Product {
public:
virtual void use() = 0;
virtual ~Product() = default;
};
class ConcreteProduct : public Product {
public:
void use() override { std::cout << "Used ConcreteProduct"; }
};
class Factory {
public:
static Product* createProduct() { return new ConcreteProduct(); }
};
❌ Nhược điểm: Tăng số lượng class, khó debug nếu không có logging rõ ràng.
Strategy
Cho phép thay đổi thuật toán runtime thông qua interface.
class SortStrategy {
public:
virtual void sort() = 0;
virtual ~SortStrategy() = default;
};
class QuickSort : public SortStrategy {
public:
void sort() override { std::cout << "QuickSort applied"; }
};
class SortContext {
private:
SortStrategy* strategy;
public:
void setStrategy(SortStrategy* s) { strategy = s; }
void execute() { strategy->sort(); }
};
❌ Nhược điểm: Phải quản lý thêm các class strategy.
Observer
Thông báo cho nhiều đối tượng khi có thay đổi trạng thái.
class Observer {
public:
virtual void update(int value) = 0;
};
class Subject {
std::vector observers;
int state = 0;
public:
void attach(Observer* obs) { observers.push_back(obs); }
void setState(int v) {
state = v;
for (auto obs : observers) obs->update(state);
}
};
❌ Nhược điểm: Khó debug lỗi thứ tự hoặc vòng lặp cập nhật.