Học C++ từ Cơ Bản đến Nâng Cao

Website tự học lập trình C++ miễn phí, thực chiến, dễ hiểu

1. OOP: Kế thừa, đa hình, đóng gói, trừu tượng

class Animal {
public:
    virtual void speak() = 0; // Pure virtual function: interface abstraction
};

class Dog : public Animal {
public:
    void speak() override { std::cout << "Woof"; }
};
    
🔸 Lưu ý: Truyền object dẫn xuất qua giá trị sẽ gây ra hiện tượng object slicing. Nên dùng con trỏ hoặc tham chiếu nếu cần đa hình.

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) {}
};
    
⚠️ Lưu ý: Tránh tạo vòng lặp vô hạn bằng cách gọi constructor chính nó hoặc lặp chuỗi constructor không kết thúc.

=default và =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
};
    
Lưu ý: Sử dụng
=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();
    
🔸 Lưu ý: Dùng từ khóa
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

// 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"
    
🔸 Đa hình tĩnh cho hiệu suất cao hơn nhưng kém linh hoạt hơn. Đa hình động linh hoạt nhưng có chi phí hiệu năng và cần quản lý đúng để tránh lỗi runtime.

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;
    

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;
}
    
⚠️ Lưu ý: Friend phá vỡ tính đóng gói, chỉ nên dùng trong các trường hợp đặc biệt như overload operator, unit test, hoặc tối ưu performance khi interface public không đủ linh hoạt.

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;
};
    
✅ Ưu điểm: Đảm bảo chỉ có 1 instance, dùng chung toàn hệ thống.
❌ 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(); }
};
    
✅ Ưu điểm: Dễ mở rộng, giảm phụ thuộc cụ thể.
❌ 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(); }
};
    
✅ Ưu điểm: Linh hoạt thay đổi thuật toán.
❌ 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);
    }
};
    
✅ Ưu điểm: Tách biệt đối tượng phát sinh thay đổi và người theo dõi.
❌ Nhược điểm: Khó debug lỗi thứ tự hoặc vòng lặp cập nhật.

← Quay lại C++ Nâng Cao