Back to blog

Phase 2: Lập Trình Hướng Đối Tượng trong Modern C++

cppc++oopprogrammingclasses
Phase 2: Lập Trình Hướng Đối Tượng trong Modern C++

Chào Mừng Đến Phase 2

Bạn đã thành thạo C++ fundamentals—types, functions, references, và compilation model. Bây giờ là lúc khám phá Lập Trình Hướng Đối Tượng trong C++. Khác với Java hay Python nơi OOP đơn giản, C++ cho bạn kiểm soát chi tiết về memory layout, object lifetime, và performance.

Phase này covers mọi thứ bạn cần để xây dựng C++ classes vững chắc: constructors, destructors, Rule of Five (và khi nào dùng Rule of Zero), inheritance, polymorphism, và operator overloading. Chúng ta sẽ nhấn mạnh RAII (Resource Acquisition Is Initialization)—idiom nền tảng C++ giúp quản lý resource an toàn và tự động.

Thời gian: 1-2 tuần, 1-2 giờ mỗi ngày Yêu cầu trước: Phase 1: C++ Fundamentals

Bạn Sẽ Học Được

✅ Thiết kế classes với proper encapsulation
✅ Viết tất cả special member functions
✅ Hiểu RAII và áp dụng cho resource management
✅ Sử dụng inheritance và virtual functions đúng cách
✅ Implement polymorphic hierarchies
✅ Overload operators cho custom types
✅ Áp dụng modern C++ idioms (= default, = delete, override, final)


1. Classes và Objects

Cú Pháp Định Nghĩa Class

class Rectangle {
private:  // Access specifier (default cho class)
    double width_;
    double height_;
 
public:
    // Constructor
    Rectangle(double width, double height)
        : width_{width}, height_{height} {}
 
    // Member functions
    double area() const {
        return width_ * height_;
    }
 
    // Getters và Setters
    double width() const { return width_; }
    void set_width(double w) { width_ = w; }
};

Điểm quan trọng:

  • Access specifiers: private, public, protected
  • Member initializer list: : width_{width} (ưu tiên hơn assignment)
  • const member functions: Hứa không modify object state

struct vs class

Sự khác biệt duy nhất: default access.

class MyClass {
    int x;  // private by default
};
 
struct MyStruct {
    int x;  // public by default
};

2. Constructors

Default Constructor

class Point {
private:
    double x_, y_;
 
public:
    // Default constructor
    Point() : x_{0.0}, y_{0.0} {}
 
    // Parameterized constructor
    Point(double x, double y) : x_{x}, y_{y} {}
};

= default và = delete

class Widget {
public:
    Widget() = default;  // Compiler tạo default constructor
    Widget(int value);   // Custom constructor
 
    // Ngăn copying
    Widget(const Widget&) = delete;
    Widget& operator=(const Widget&) = delete;
};

Member Initializer Lists

Luôn ưu tiên initializer lists hơn assignment:

class GoodExample {
private:
    std::string name_;
    int count_;
 
public:
    GoodExample(const std::string& name, int count)
        : name_{name}, count_{count} {}  // Direct initialization
};

explicit Keyword

Ngăn implicit conversions:

class Kilometer {
public:
    explicit Kilometer(double v) : value_{v} {}
};
 
// Kilometer k = 5.0;  // ERROR: không implicit conversion
Kilometer k{5.0};      // OK: explicit construction

3. Destructors và RAII

RAII: Resource Acquisition Is Initialization

RAII là idiom quan trọng nhất trong C++: gắn resource lifetime với object lifetime.

class FileHandle {
private:
    FILE* file_;
 
public:
    explicit FileHandle(const char* filename, const char* mode) {
        file_ = std::fopen(filename, mode);
        if (!file_) {
            throw std::runtime_error("Failed to open file");
        }
    }
 
    ~FileHandle() {
        if (file_) {
            std::fclose(file_);  // Luôn được cleanup!
        }
    }
 
    // Ngăn copying
    FileHandle(const FileHandle&) = delete;
    FileHandle& operator=(const FileHandle&) = delete;
};

Tại sao RAII mạnh:

  1. Không leak: Destructor luôn chạy (kể cả với exceptions)
  2. Không quên đóng: Cleanup tự động
  3. Composable: RAII objects có thể chứa RAII objects khác

4. Copy và Move Semantics

Copy Constructor và Assignment

class IntArray {
private:
    int* data_;
    size_t size_;
 
public:
    // Copy constructor
    IntArray(const IntArray& other)
        : data_{new int[other.size_]}, size_{other.size_} {
        std::copy(other.data_, other.data_ + size_, data_);
    }
 
    // Copy assignment operator
    IntArray& operator=(const IntArray& other) {
        if (this != &other) {
            delete[] data_;
            size_ = other.size_;
            data_ = new int[size_];
            std::copy(other.data_, other.data_ + size_, data_);
        }
        return *this;
    }
};

Move Semantics

class IntArray {
public:
    // Move constructor
    IntArray(IntArray&& other) noexcept
        : data_{other.data_}, size_{other.size_} {
        other.data_ = nullptr;
        other.size_ = 0;
    }
 
    // Move assignment
    IntArray& operator=(IntArray&& other) noexcept {
        if (this != &other) {
            delete[] data_;
            data_ = other.data_;
            size_ = other.size_;
            other.data_ = nullptr;
            other.size_ = 0;
        }
        return *this;
    }
};

Rule of Five và Rule of Zero

Rule of Five: Nếu định nghĩa một trong những hàm sau, bạn có thể cần định nghĩa tất cả:

  1. Destructor
  2. Copy constructor
  3. Copy assignment operator
  4. Move constructor
  5. Move assignment operator

Rule of Zero: Thiết kế classes để compiler-generated defaults đúng (dùng RAII types).


5. Inheritance

Basic Inheritance

class Animal {
protected:
    std::string name_;
 
public:
    Animal(std::string name) : name_{std::move(name)} {}
    void eat() const { std::cout << name_ << " is eating\n"; }
};
 
class Dog : public Animal {
public:
    Dog(std::string name) : Animal{std::move(name)} {}
    void bark() const { std::cout << name_ << " says: Woof!\n"; }
};

Thứ Tự Constructor/Destructor

Khởi tạo: Base() → Derived()
Hủy: ~Derived() → ~Base()

6. Polymorphism

Virtual Functions

class Shape {
public:
    virtual double area() const = 0;  // Pure virtual
    virtual ~Shape() = default;       // QUAN TRỌNG: virtual destructor
};
 
class Circle : public Shape {
private:
    double radius_;
 
public:
    explicit Circle(double r) : radius_{r} {}
 
    double area() const override {
        return 3.14159 * radius_ * radius_;
    }
};

Virtual Destructors

Quy tắc quan trọng: Nếu class có virtual functions, cần virtual destructor.

class Base {
public:
    virtual void foo() {}
    virtual ~Base() = default;  // ĐÚNG
};

override và final

class Derived : public Base {
public:
    void foo() const override;  // Đảm bảo đang override
 
    void bar() final;  // Không thể override thêm
};
 
class FinalClass final : public Base {
    // Không thể kế thừa từ class này
};

7. Operator Overloading

class Fraction {
private:
    int numerator_;
    int denominator_;
 
public:
    Fraction(int num, int den = 1)
        : numerator_{num}, denominator_{den} {}
 
    // Arithmetic operators
    Fraction operator+(const Fraction& other) const {
        return Fraction{
            numerator_ * other.denominator_ + other.numerator_ * denominator_,
            denominator_ * other.denominator_
        };
    }
 
    // Comparison (C++20)
    auto operator<=>(const Fraction& other) const {
        return (numerator_ * other.denominator_) <=>
               (other.numerator_ * denominator_);
    }
};
 
// Stream operator (non-member)
std::ostream& operator<<(std::ostream& os, const Fraction& f) {
    return os << f.numerator() << '/' << f.denominator();
}

8. Multiple Inheritance và Diamond Problem

Virtual Inheritance

Giải quyết diamond problem:

class Animal {
public:
    std::string name;
};
 
class Mammal : virtual public Animal {};
class Bird : virtual public Animal {};
 
class Bat : public Mammal, public Bird {
    // Chỉ có MỘT Animal subobject
};

Lời khuyên: Tránh multiple inheritance của classes. Ưu tiên:

  1. Single inheritance với interfaces (pure abstract classes)
  2. Composition over inheritance

9. Các Lỗi Thường Gặp

1. Slicing

void badFunction(Base b) {  // Pass by VALUE = slicing!
    b.foo();  // Luôn gọi Base::foo()
}

Fix: Pass polymorphic types bằng reference hoặc pointer.

2. Quên Virtual Destructor

Luôn làm destructors virtual trong base classes.

3. Gọi Virtual Functions trong Constructor/Destructor

Virtual dispatch không hoạt động như mong đợi trong constructors.


Tóm Tắt

✅ Classes: Dùng access specifiers, prefer struct cho data, class cho behavior
✅ Constructors: Dùng initializer lists, explicit cho single-arg
✅ RAII: Gắn resource lifetime với object lifetime
✅ Rule of Five/Zero: Định nghĩa tất cả năm, hoặc không định nghĩa gì
✅ Inheritance: Dùng public inheritance, luôn virtual destructors
✅ Polymorphism: Dùng override, pass by reference/pointer
✅ Composition: Ưu tiên composition hơn inheritance

Tiếp Theo

Tài Liệu Bổ Sung


Series: Modern C++ Learning Roadmap
Trước: Phase 1: C++ Fundamentals
Tiếp: Phase 3: Modern C++ Features

📬 Đăng ký nhận bản tin

Nhận những bài viết mới nhất qua email mỗi tuần. Không spam, hủy đăng ký bất cứ lúc nào.

Chúng tôi tôn trọng quyền riêng tư của bạn. Hủy đăng ký bất cứ lúc nào.

💬 Comments

Đăng nhập để bình luận

Chúng tôi sẽ không đăng bất cứ điều gì mà không có sự cho phép của bạn.