C++ hiện đại và cách làm tương ứng trong C

Lập trình hướng đối tượng (OOP)

Học đóng gói, kế thừa, ghi đè và đa hình trong C++, đồng thời so sánh với thiết kế C tường minh.

Lập trình hướng đối tượng trong C++ là gì?

OOP tổ chức chương trình quanh các đối tượng có trạng thái, quy tắc bất biến và hành vi. C++ hỗ trợ mô hình này bằng class, access control, inheritance và virtual dispatch; C phải biểu diễn các cơ chế đó một cách tường minh.

Bốn nguyên lý cốt lõi của OOP

  • Đóng gói: class bảo vệ invariant bằng cách kiểm soát quyền truy cập trạng thái bên trong.
  • Trừu tượng: code phía gọi phụ thuộc vào public interface nhỏ gọn thay vì chi tiết lưu trữ và triển khai.
  • Kế thừa: derived class mở rộng hoặc chuyên biệt hóa một hợp đồng base class hợp lệ.
  • Đa hình: virtual call chọn hành vi của object thực tế tại runtime.

C và C++ biểu diễn thiết kế như thế nào

C

C đặt dữ liệu trong struct và lưu hành vi bằng con trỏ hàm. Quy ước cùng các hàm API phải tự đảm bảo invariant, ownership và dispatch hợp lệ.

C++

C++ giữ balance ở private, kế thừa giao diện Account chung và override virtual operation. Compiler kiểm tra quyền truy cập và chữ ký override.

Gợi ý thiết kế: Base class đa hình cần virtual destructor; tránh raw pointer sở hữu tài nguyên và ưu tiên composition nếu derived type không thực sự thỏa hợp đồng của base class.

Code ví dụ bằng C và C++

C
Chạy code →
main.c
#include <stdio.h>

typedef struct Account Account;
typedef void (*MonthEnd)(Account *account);

struct Account {
    const char *owner;
    double balance;
    double interest_rate;
    MonthEnd month_end;
};

void standard_month_end(Account *account) {
    (void)account;
}

void savings_month_end(Account *account) {
    account->balance *= 1.0 + account->interest_rate;
}

void print_account(Account *account) {
    account->month_end(account);
    printf("%s: %.2f\n", account->owner, account->balance);
}

int main(void) {
    Account accounts[] = {
        {"Ada", 150.0, 0.0, standard_month_end},
        {"Lin", 200.0, 0.05, savings_month_end}
    };

    for (int i = 0; i < 2; ++i) {
        print_account(&accounts[i]);
    }
    return 0;
}
Kết quả khi chạy
Ada: 150.00
Lin: 210.00
C++
Chạy code →
main.cpp
#include <iomanip>
#include <iostream>
#include <string>
#include <utility>
#include <vector>

class Account {
    std::string owner_;
    double balance_;

protected:
    void add_balance(double amount) { balance_ += amount; }

public:
    Account(std::string owner, double balance)
        : owner_(std::move(owner)), balance_(balance) {}

    virtual ~Account() = default;
    virtual void month_end() {}

    const std::string& owner() const { return owner_; }
    double balance() const { return balance_; }
};

class SavingsAccount final : public Account {
    double interest_rate_;

public:
    SavingsAccount(std::string owner, double balance, double rate)
        : Account(std::move(owner), balance), interest_rate_(rate) {}

    void month_end() override {
        add_balance(balance() * interest_rate_);
    }
};

int main() {
    Account standard("Ada", 150.0);
    SavingsAccount savings("Lin", 200.0, 0.05);
    std::vector<Account *> accounts{&standard, &savings};

    std::cout << std::fixed << std::setprecision(2);
    for (Account *account : accounts) {
        account->month_end();
        std::cout << account->owner() << ": "
                  << account->balance() << '\n';
    }
}
Kết quả khi chạy
Ada: 150.00
Lin: 210.00

So sánh C và C++

Bản C lưu trạng thái trong struct và chọn hành vi cuối tháng qua con trỏ hàm. Bản C++ giữ số dư ở private, kế thừa một giao diện chung và dùng virtual override. Cả hai cho phép code phía gọi sử dụng một thao tác mà không cần kiểm tra loại tài khoản.

C

C dùng quy ước đặt tên, callback, macro và struct chứa ngữ cảnh.

C++

C++ có sẵn các công cụ trừu tượng có phạm vi rõ ràng, an toàn kiểu và thường không tốn thêm chi phí khi chạy.

Bài tập mở rộng

Hãy chạy cả hai phiên bản, sau đó thử thay đổi code để quan sát sự khác biệt về mức độ an toàn mà mỗi ngôn ngữ mang lại.

  • Tự viết cơ chế bằng C trước, rồi mới dùng tính năng tương ứng của C++.
  • Thay việc giải phóng tài nguyên thủ công bằng RAII.
  • Kiểm tra xem tính năng có phát sinh cấp phát bộ nhớ hoặc lời gọi hàm ảo hay không.

Luyện tập với thử thách

Áp dụng kiến thức của bài học này để giải một bài toán thực tế. Mỗi thử thách chạy ngay trên trình duyệt, có kiểm thử tức thì và AI nhận xét.

Tất cả thử thách