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).
Vì sao cần header file?
Phần tiêu đề “Vì sao cần header file?”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).
Tạo và dùng header file
Phần tiêu đề “Tạo và dùng header file”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);
#endifmath_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:
g++ main.cpp math_utils.cpp -o programHeader guard: tránh khai báo trùng lặp
Phần tiêu đề “Header guard: tránh khai báo trùng lặp”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);<...> vs "..." trong #include
Phần tiêu đề “<...> vs "..." trong #include”#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 projectNamespace: tránh xung đột tên
Phần tiêu đề “Namespace: tránh xung đột tên”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ể.
std là một namespace
Phần tiêu đề “std là một namespace”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.
using namespace: dùng cẩn thận
Phần tiêu đề “using namespace: dùng cẩn thận”#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 stdusing std::endl;
int main() { cout << "Hello!" << endl; return 0;}Tổng kết
Phần tiêu đề “Tổng kết”- Header file (
.h/.hpp) chứa khai báo, được nhiều file.cppdùng chung thông qua#include - Header guard (
#ifndef/#define/#endifhoặ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