Ch.1 Tổng quan & Kiến trúc cơ bản OOP
1. OOP giải quyết hạn chế nào của lập trình hướng thủ tục?
  • Hướng thủ tục: dữ liệu và hàm tách rời, khó quản lý khi chương trình lớn, khó tái sử dụng
  • OOP: gom dữ liệu + hành vi vào đối tượng → dễ bảo trì, tái sử dụng, mở rộng
2. Phân biệt Lớp (Class) và Đối tượng (Object). public/private thể hiện tính chất nào?
  • Class = bản thiết kế (khuôn mẫu). Object = thực thể cụ thể tạo từ class
  • private/public → thể hiện Tính đóng gói (Encapsulation): che giấu dữ liệu, chỉ cho truy cập qua phương thức public
3. Constructor mặc định / có tham số / Copy Constructor. Shallow copy vs Deep copy?
  • Mặc định: không tham số, gán giá trị ban đầu
  • Có tham số: nhận giá trị từ bên ngoài để khởi tạo
  • Copy Constructor: tạo đối tượng MỚI từ đối tượng ĐÃ CÓ. VD: PhanSo b = a; hoặc PhanSo b(a);
  • Shallow copy: chỉ copy địa chỉ con trỏ → 2 đối tượng trỏ cùng 1 vùng nhớ → xóa 1 cái, cái kia hỏng
  • Deep copy: cấp phát vùng nhớ MỚI, sao chép giá trị sang → độc lập hoàn toàn
4. "Rule of Three" là gì? const và static trong lớp?
  • Rule of Three: Nếu lớp cần 1 trong 3 thì phải viết cả 3: Copy Constructor, operator=, Destructor
  • const method: cam kết KHÔNG thay đổi thuộc tính của đối tượng
  • static: thuộc tính/phương thức được chia sẻ chung cho TẤT CẢ đối tượng của lớp (không thuộc riêng đối tượng nào)
Ch.2 Nạp chồng toán tử & Hàm bạn
1. Hàm bạn (friend function) là gì? Khi nào bắt buộc dùng?
  • Hàm bên ngoài lớp nhưng được phép truy cập thành phần private của lớp
  • Bắt buộc khi nạp chồng << và >> vì tham số bên trái là ostream/istream, không phải đối tượng của lớp
2. Nạp chồng <<, >>, = và phân biệt operator= với Copy Constructor
// Nạp chồng <<
friend ostream& operator<<(ostream& os, const X& x);
// Nạp chồng >>
friend istream& operator>>(istream& is, X& x);
// Nạp chồng =
X& operator=(const X& other);
  • Copy Constructor: gọi khi tạo đối tượng MỚI từ đối tượng cũ. VD: PhanSo b = a; (b chưa tồn tại)
  • operator=: gọi khi gán giữa 2 đối tượng ĐỀU ĐÃ TỒN TẠI. VD: b = a; (b đã tồn tại từ trước)
Ch.3 Kế thừa & Đa kế thừa
1. Phân biệt các chế độ kế thừa public, protected, private
Lớp chapublicprotectedprivate
publicpublicprotectedprivate
protectedprotectedprotectedprivate
privateKhông truy cập được từ lớp con
2. Thứ tự gọi Constructor và Destructor trong kế thừa
  • Tạo đối tượng: Constructor lớp CHA trước → lớp CON sau
  • Hủy đối tượng: Destructor lớp CON trước → lớp CHA sau (ngược lại)
Nhớ: Xây nhà từ móng (cha) lên. Phá nhà từ nóc (con) xuống.
3. Đa kế thừa & "Vấn đề kim cương" (Diamond Problem)
  • Đa kế thừa: 1 lớp kế thừa từ nhiều lớp cùng lúc
  • Diamond Problem: D kế thừa B và C, B và C cùng kế thừa A → D có 2 bản sao của A → mơ hồ
  • Giải quyết: Lớp cơ sở ảo (Virtual Base Class)
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};
// D chỉ có 1 bản sao duy nhất của A
4. Upcasting và Downcasting. dynamic_cast
  • Upcasting: con trỏ lớp cha trỏ đến lớp con. An toàn, tự động. VD: Base* p = new Derived();
  • Downcasting: con trỏ lớp con trỏ đến lớp cha. Cần dynamic_cast, trả về nullptr nếu thất bại
Derived* d = dynamic_cast<Derived*>(basePtr);
if (d != nullptr) { // thành công }
Ch.4 Đa hình, Lớp trừu tượng & SOLID
1. Đa hình động. virtual, override, final
  • Đa hình động = virtual function + con trỏ/tham chiếu lớp cơ sở → gọi đúng hàm của lớp con tại runtime
  • virtual: cho phép lớp con ghi đè
  • override (C++11): đánh dấu rõ ràng đang ghi đè, compiler kiểm tra
  • final (C++11): ngăn lớp con ghi đè hàm này / ngăn kế thừa lớp này
2. Hàm ảo thuần túy & Lớp trừu tượng
  • Hàm ảo thuần túy: virtual void f() = 0; — không có thân hàm, bắt buộc lớp con phải ghi đè
  • Lớp trừu tượng: có ít nhất 1 hàm ảo thuần túy → KHÔNG THỂ tạo đối tượng trực tiếp
3. Virtual Destructor - Tại sao cực kỳ quan trọng?
  • Khi delete con trỏ lớp cha trỏ đến lớp con:
  • Không có virtual destructor → chỉ gọi ~Base() → BỎ QUA ~Derived() → rò rỉ bộ nhớ
  • Có virtual ~Base() {} → gọi ~Derived() trước, rồi ~Base() → an toàn
class Hinh {
public:
    virtual ~Hinh() {}  // BẮT BUỘC khi dùng đa hình
    virtual double TinhDienTich() = 0;
};

int main() {
    Hinh* h = new HinhTron(5.0);  // upcasting
    cout << h->TinhDienTich();    // đa hình
    delete h;                     // an toàn nhờ virtual destructor
}
4. 5 nguyên lý SOLID
ChữTênÝ nghĩa ngắn gọn
SSingle ResponsibilityMỗi lớp chỉ 1 trách nhiệm duy nhất
OOpen/ClosedMở để mở rộng, đóng để sửa đổi
LLiskov SubstitutionLớp con thay thế được lớp cha mà không làm sai
IInterface SegregationChia nhỏ interface, không ép phụ thuộc thứ không cần
DDependency InversionPhụ thuộc trừu tượng, không phụ thuộc cụ thể
Case Case Study: Quản lý Thư viện
// 1. Lớp trừu tượng AnPham
class AnPham {
protected:
    string maAnPham, tieuDe;
    double giaBan;
public:
    AnPham(string ma, string td, double gia)
        : maAnPham(ma), tieuDe(td), giaBan(gia) {}
    virtual ~AnPham() {}  // Virtual Destructor
    virtual void XuatThongTin() = 0;  // Pure virtual
};

// 2. Lớp Sach kế thừa AnPham
class Sach : public AnPham {
private:
    int soTrang;
public:
    Sach(string ma, string td, double gia, int st)
        : AnPham(ma, td, gia), soTrang(st) {}
    void XuatThongTin() override {
        cout << maAnPham << " - " << tieuDe
             << " - " << giaBan << " - " << soTrang << endl;
    }
    // 3. Nạp chồng << và >>
    friend ostream& operator<<(ostream& os, const Sach& s) {
        os << s.maAnPham << " - " << s.tieuDe;
        return os;
    }
    friend istream& operator>>(istream& is, Sach& s) {
        is >> s.maAnPham >> s.tieuDe >> s.giaBan >> s.soTrang;
        return is;
    }
};

// Lớp TapChi tương tự
class TapChi : public AnPham {
private:
    int thangPhatHanh;
public:
    TapChi(string ma, string td, double gia, int thang)
        : AnPham(ma, td, gia), thangPhatHanh(thang) {}
    void XuatThongTin() override {
        cout << maAnPham << " - " << tieuDe
             << " - Tháng " << thangPhatHanh << endl;
    }
};

// 4. Đa hình & Quản lý bộ nhớ
int main() {
    AnPham* ds[3];  // Mảng con trỏ lớp cơ sở
    ds[0] = new Sach("S01", "C++", 50000, 300);  // upcasting
    ds[1] = new TapChi("T01", "CNTT", 20000, 6);
    ds[2] = new Sach("S02", "OOP", 60000, 450);

    for (int i = 0; i < 3; i++)
        ds[i]->XuatThongTin();  // Đa hình động

    for (int i = 0; i < 3; i++)
        delete ds[i];  // An toàn nhờ virtual destructor
    return 0;
}

Trắc nghiệm OOP

50 câu hỏi trắc nghiệm - Kiểm tra kiến thức trước khi thi

Chọn chương muốn làm (hoặc tất cả):

Câu 1/50 Đúng: 0
0% 0/0