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

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

C++ hỗ trợ class và virtual function trực tiếp; C có thể mô phỏng quan hệ tương tự bằng struct, hàm có kiểm soát và con trỏ hàm.

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;
}
Output dự kiến
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';
    }
}
Output dự kiến
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 naming convention, callback, macro và context struct.

C++

Tính năng ngôn ngữ cung cấp abstraction có scope, type-safe và thường zero-overhead.

Bài tập mở rộng

Chạy cả hai phiên bản rồi thay đổi để quan sát khác biệt về bảo đảm của từng ngôn ngữ.

  • Viết cơ chế bằng C trước abstraction C++.
  • Loại bỏ manual cleanup bằng RAII.
  • Kiểm tra allocation hoặc virtual dispatch phát sinh.