0

[C++ OOP Thực Chiến] Bài 7: Khai báo Class chuẩn dự án - Tạm biệt "nồi lẩu" main.cpp!

Chào anh em! Nếu anh em đã đi cùng mình qua 6 bài trước và nắm vững 4 trụ cột của OOP, thì xin chúc mừng, anh em đã có tư duy của một Kỹ sư phần mềm.

Nhưng tư duy tốt mà cách trình bày code lại giống như một "nồi lẩu thập cẩm" – nhét hàng tá Class, hàng ngàn dòng code logic vào chung một file main.cpp – thì khi mang dự án lên công ty, anh em chắc chắn sẽ bị Tech Lead "gõ đầu".

Tại sao ư?

  1. Compile cực chậm: Mỗi lần sửa một dòng code nhỏ, C++ sẽ phải dịch lại toàn bộ cái file khổng lồ đó.
  2. Git Conflict: Cả team 5 người cùng đè vào sửa chung một file main.cpp, lúc gộp code (merge) sẽ là một thảm họa đẫm máu.

Để giải quyết, chúng ta phải học cách Khai báo Class tách biệt: Chia Class ra thành file Header (.h) và file Source (.cpp).

1. File Header (.h) - "Bản Hợp Đồng" (Interface)

File Header là nơi chúng ta Khai báo (Declare) Class. Nó đóng vai trò như một bản hợp đồng, một cái Menu nhà hàng. Nó chỉ cho người khác biết Class này có những dữ liệu gì, có những chức năng gì, nhưng tuyệt đối không chứa logic code bên trong.

Một file Header chuẩn luôn phải có Include Guard (Bảo vệ include chéo) để tránh việc file bị copy nhiều lần gây lỗi biên dịch. Ngày nay, C++ hiện đại hỗ trợ cú pháp #pragma once cực kỳ ngắn gọn.

Hãy thử khai báo một hệ thống Logger (Ghi log hệ thống):

// --- File: Logger.h ---
#pragma once // Chống include file này nhiều lần

#include <string>

class Logger {
private:
    std::string logFile; // Nơi lưu tên file
    int errorCount;      // Đếm số lượng lỗi

public:
    // Khai báo Constructor (Chỉ ghi tên, KHÔNG viết logic ngoặc nhọn {})
    Logger(std::string fileName);

    // Khai báo các phương thức (Nhớ chấm phẩy ở cuối)
    void info(std::string message);
    void error(std::string message);
    int getErrorCount();
};

Nhìn vào file .h này, một đồng nghiệp khác trong team ngay lập tức hiểu Class Logger có thể làm gì mà không cần phải căng mắt đọc hàng đống lệnh if-else phức tạp.

2. File Source (.cpp) - "Xưởng Gia Công" (Implementation)

Đây là nơi chúng ta Định nghĩa (Define) chi tiết các hàm đã khai báo ở Header. Để báo cho máy tính biết "Hàm này thuộc về Class nào?", chúng ta sử dụng Toán tử phân giải phạm vi :: (Scope Resolution Operator).

// --- File: Logger.cpp ---
#include "Logger.h" // PHẢI include file Header của chính nó
#include <iostream>

// Định nghĩa Constructor: TênClass::TênHàm
Logger::Logger(std::string fileName) {
    logFile = fileName;
    errorCount = 0;
    std::cout << "[INIT] Da khoi tao Logger. Ghi vao file: " << logFile << "\n";
}

// Định nghĩa hàm info
void Logger::info(std::string message) {
    std::cout << "[INFO] " << message << "\n";
}

// Định nghĩa hàm error
void Logger::error(std::string message) {
    std::cout << "[ERROR] " << message << "\n";
    errorCount++; // Tăng biến đếm lỗi
}

// Định nghĩa hàm getErrorCount
int Logger::getErrorCount() {
    return errorCount;
}

3. Ráp nối hệ thống tại Client Code (main.cpp)

Bây giờ, file main.cpp của bạn sẽ trở nên cực kỳ "sạch sẽ". Người dùng (Client) chỉ cần "nhập khẩu" cái Menu (.h) là có thể xài được các chức năng đã đúc sẵn.

// --- File: main.cpp ---
#include <iostream>
#include "Logger.h" // Chỉ include file .h, không include file .cpp

using namespace std;

int main() {
    cout << "--- HE THONG KHOI DONG ---\n";
    
    // Khởi tạo Object
    Logger sysLog("server_runtime.log");

    // Gọi các phương thức
    sysLog.info("He thong khoi dong thanh cong.");
    sysLog.info("Dang ket noi Database...");
    sysLog.error("Khong tim thay Database!");

    cout << "Tong so loi phat hien: " << sysLog.getErrorCount() << "\n";

    return 0;
}

Thành quả: Dự án của bạn giờ đây đã được module hóa. Nhóm A có thể code class Logger, nhóm B code class Database, mọi thứ độc lập và chuyên nghiệp y như các thư viện C++ ngoài đời thực!

Tạm kết & Gợi mở

Việc tách biệt Khai báo (.h) và Định nghĩa (.cpp) là quy tắc sống còn khi làm việc nhóm với C++. Nó giúp ẩn đi độ phức tạp (như ta đã học ở bài Abstraction) và tối ưu hóa thời gian build project.

Trong code demo ở trên, chúng ta đã định nghĩa các phương thức trong file .cpp một cách rất cơ bản. Nhưng thực tế đi làm, việc viết ruột cho một phương thức (Method Definition) trong C++ lại tiềm ẩn rất nhiều quyền năng đáng sợ:

  • Làm sao để viết một phương thức siêu tốc không cần tốn thời gian gọi hàm? (inline)
  • Làm sao để cam kết một phương thức tuyệt đối không thay đổi dữ liệu của Class? (const method)
  • Khai báo một hàm dùng chung cho mọi Object mà không cần tạo Object thì làm thế nào? (static)

Tất cả những bí thuật này sẽ được giải mã trong bài tiếp theo. Hẹn gặp lại anh em ở Bài 8: Định nghĩa phương thức của Class - Khai phá sức mạnh của inline, const và static. Nhớ Upvote và Share series để ủng hộ mình nhé!


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.