Bỏ qua để đến nội dung

Header file và Namespace

Khi chương trình lớn dần, việc viết tất cả code trong một file duy nhất trở nên khó quản lý. C++ giải quyết vấn đề này bằng header file (để chia sẻ khai báo giữa các file) và namespace (để tránh xung đột tên).

Một chương trình C++ thực tế thường được chia thành nhiều file .cpp, mỗi file đảm nhiệm một phần chức năng riêng. Để các file này có thể “biết” về hàm/class được định nghĩa ở file khác, ta dùng header file (.h hoặc .hpp) chứa các khai báo (declaration).

math_utils.h (khai báo - declaration):

#ifndef MATH_UTILS_H
#define MATH_UTILS_H
int add(int a, int b);
int multiply(int a, int b);
#endif

math_utils.cpp (định nghĩa - definition):

#include "math_utils.h"
int add(int a, int b) {
return a + b;
}
int multiply(int a, int b) {
return a * b;
}

main.cpp (sử dụng):

#include <iostream>
#include "math_utils.h"
int main() {
std::cout << add(2, 3) << std::endl; // 5
std::cout << multiply(2, 3) << std::endl; // 6
return 0;
}

Biên dịch cả hai file .cpp cùng lúc:

Terminal window
g++ main.cpp math_utils.cpp -o program

Dòng #ifndef MATH_UTILS_H / #define MATH_UTILS_H / #endif được gọi là header guard, đảm bảo nội dung header chỉ được đưa vào (include) đúng một lần, dù nhiều file .cpp cùng #include header đó. Thiếu header guard sẽ gây lỗi biên dịch “khai báo lại” (redefinition) khi một header vô tình được include nhiều lần (thường xảy ra gián tiếp qua các header khác).

Cách hiện đại, ngắn gọn hơn để đạt cùng mục đích:

#pragma once // Tương đương header guard, được hầu hết trình biên dịch hiện đại hỗ trợ
int add(int a, int b);
#include <iostream> // Dùng <> cho thư viện chuẩn / thư viện bên ngoài
#include "math_utils.h" // Dùng "" cho header do chính bạn viết, trong cùng project

Khi project lớn, hoặc dùng nhiều thư viện khác nhau, có thể xảy ra tình trạng hai hàm/class trùng tên. Namespace giải quyết vấn đề này bằng cách “nhóm” các định danh lại dưới một tên chung:

#include <iostream>
namespace MathLibrary {
int add(int a, int b) {
return a + b;
}
}
namespace StringLibrary {
std::string add(std::string a, std::string b) { // Cùng tên "add" nhưng khác namespace
return a + b;
}
}
int main() {
std::cout << MathLibrary::add(2, 3) << std::endl; // 5
std::cout << StringLibrary::add("Hello, ", "World!") << std::endl; // Hello, World!
return 0;
}

Toán tử :: (scope resolution operator) dùng để truy cập một định danh bên trong namespace cụ thể.

Tất cả những gì bạn đã dùng như std::cout, std::string, std::vector đều nằm trong namespace std - đây là quy ước của toàn bộ thư viện chuẩn C++, nhằm tránh xung đột với tên do bạn tự đặt.

#include <iostream>
using namespace std; // Không cần viết "std::" nữa
int main() {
cout << "Hello!" << endl; // Thay vì std::cout, std::endl
return 0;
}

using namespace std; giúp code ngắn gọn hơn, nhưng không nên dùng trong header file hoặc project lớn, vì nó “mở toang” toàn bộ tên trong namespace std vào phạm vi hiện tại, dễ gây xung đột tên khó lường khi project phát triển. Cách an toàn hơn là chỉ import những gì cần dùng:

using std::cout; // Chỉ "mở" riêng cout, không phải toàn bộ namespace std
using std::endl;
int main() {
cout << "Hello!" << endl;
return 0;
}
  • Header file (.h/.hpp) chứa khai báo, được nhiều file .cpp dùng chung thông qua #include
  • Header guard (#ifndef/#define/#endif hoặc #pragma once) ngăn nội dung header bị đưa vào nhiều lần
  • Namespace nhóm các định danh lại để tránh xung đột tên, truy cập bằng toán tử ::
  • Tránh dùng using namespace std; trong project lớn hoặc trong header file, vì dễ gây xung đột tên