[Học lập trình C++] Chương 1: 1.9 – Header file
1.9 – Header file
Headers, và mục đích của chúng
Khi một chương trình
phát triển càng ngày càng lớn (và include nhiều file), nó trở nên mệt mỏi khi
phải khai báo trước mọi hàm mà bạn muốn dùng nằm ở file khác. Nếu bạn có thể đặt
tất cả khai báo vào một nơi thì có phải tốt hơn không?
C++ code file (với
đuôi mở rộng .cpp) không phải là những file phổ biến trong chương trình C++. Kiểu
khác của file được gọi là một header file, thỉnh thoảng được biết đến với tên gọi
include file. Header file thông thường của đuôi .h, nhưng bạn sẽ thỉnh thoảng gặp
những đuôi .hpp hoặc không có đuôi. Mục đích của header file là để lưu các khai
báo cho các file khác sử dụng.
Sử dụng header file của thư viện chuẩn
Quan sát chương trình
sau:
#include <iostream>
int main()
{
using namespace std;
cout << "Hello,
world!" << endl;
return 0;
}
Chương trình này in
ra “hello world!” ra màn hình console sử dụng cout. Tuy nhiên, chương trình này
chưa định nghĩa cout, vậy làm thế nào mà compiler biết được cout là gì? Câu trả
lời là cout được khai báo trong một file gọi là iostream. Khi chúng ta sử dụng
dòng #include <iostream>, chúng ta đang yêu cầu rằng tất cả nội dung từ
file header tên là “iostream” được copy sang file include nó. Điều đó làm cho nọi
dung từ file header có sẵn trong code file của chúng ta.
Hãy nhớ rằng header
file thông thường chỉ chưa khai báo. Chúng không định nghĩa việc thực thi như
thế nào. Vì vậy nếu cout chỉ được khai báo trong “iostream” header file, vậy
thì nơi nào nó thực sự được định nghĩa? Nó được hiện thực trong thư viện chuẩn
runtime C++, cái mà tự động được link vào chương trình của bạn trong quá trình
linking.
Hãy xem chuyện gì sẽ
xảy ra nếu iostream header không tồn tại. Nơi nào bạn dùng std::cout, bạn phải
copy thủ công vào tất cả khai báo liên quan đến std::cout vào phần trên cùng của
mỗi file sử dụng nó! Điều này yêu cầu kiến thức về cái nào thích hợp và cái nào
không. Và thật đơn giản biết bao nếu ta chỉ cần #include iostream!
Tự viết header riêng!
Bây giờ hay quay trở
lại ví dụ chúng ta đã thảo luận trong bài học trước. Chúng ta có hai file,
add.cpp và main.cpp.
add.cpp:
1
2
3
4
|
int add(int x, int y)
{
return x + y;
}
|
main.cpp:
1
2
3
4
5
6
7
8
9
10
|
#include <iostream>
int add(int x, int y); // forward declaration using function
prototype
int main()
{
using namespace std;
cout << "The
sum of 3 and 4 is " << add(3, 4) << endl;
return 0;
}
|
Chúng ta đã sử dụng
tiền khai báo để trình biên dịch biết add là gì khi biên dịch main.cpp. Như đã
đề cập trước đó, viết tiền khai báo cho mỗi hàm bạn muốn dùng nằm trong file
khác có thể mau chán.
Header file có thể
giúp ta thoát khỏi gánh nặng này. Một header file chỉ cần viết một lần và có thể
được include ở nhiều file khác nếu cần. Đây cũng giúp duy trì ít nhất số lần
thay đổi. Viết file header riêng thì dễ đến bất ngờ. Header file tồn tại 2 phần.
Phần đầu tiên được gọi
là header guard, chúng ta sẽ thảo luận nó ở chương tiếp theo. Header guards
ngăn ngừa việc included nhiều lần trong cùng một file.
Phần thứ hai là nội
dung thực của file .h, nên được khai báo cho tất cả hàm chúng ta muốn file khác
có thể nhìn thấy. Header file nên có đuôi .h, vì vậy chúng ta sẽ gọi file
header mới là add.h:
add.h:
1
2
3
4
5
6
7
8
9
|
// This is start of the header guard. ADD_H can be any unique
name. By convention, we use the name of the header
file.
#ifndef ADD_H
#define ADD_H
// This is the content of the .h file, which is where the
declarations go
int add(int x, int y); // function prototype for add.h -- don't
forget the semicolon!
// This is the end of the header guard
#endif
|
Để sử dụng header
file này trong main.cpp, chúng ta phải include nó.
Main.cpp mà include
add.h:
#include <iostream>
#include "add.h"
int main()
{
using namespace std;
cout << "The sum of
3 and 4 is " << add(3, 4) << endl;
return 0;
}
Add.cpp cũng tương tự:
int add(int x, int y)
{
return x + y;
}
Khi trình biên dịch
biên dịch đến dòng #include “add.h”, nó sao chép nội dung của add.h vào file hiện
tại ở điểm đó. Bởi vì file add.h chứa nguyên mẫu hàm của add(). Nguyên mẫu này
được sử dụng như tiền khai báo của add()!
Do đó chương trình sẽ
biên dịch và link đúng cách.
Chú ý: Khi bạn
include một file, toàn bộ nội dung của file included được chèn vào điểm mà
chúng ta include
Nếu bạn xuất hiện lỗi
biên dịch về add.h không được tìm thấy, chắc chắn file có thực tên là add.h hay
không. Phụ thuộc vào cách bạn tạo và đặt tên nó, có thể file được đặt tên mà
không có đuôi mở rộng “add” hoặc “add.h.txt” hoặc “add.hpp”.
Nếu bạn xuất hiện một
lỗi linker về add() chưa được định nghĩa, chắc chắn rằng bạn đã include add.cpp
vào trong project đúng cách.
Dấu ngoặc nhọn <> và dấu nháy “”
Bạn có thể tò mò tại
sao chúng ta sử dụng dấu ngoặc nhọn cho iostream và dấu nháy kép cho add.h. Câu
trả lời là dấu ngoặc nhọn được sử dụng để nói với trình biên dịch rằng chúng ta
đang include một header file mà đã được include sẵn trong trình biên dịch. Vì vậy
nó sẽ tìm kiếm những header file trong đường dẫn của hệ thống. Dấu nháy kép nói
cho trình biên dịch rằng đây là header mà chúng ta cung cấp, vì vậy nó sẽ tìm
kiếm header file trong đường dẫn hiện tại chứa file mã nguồn trước. Nếu nó
không tìm thấy header file ở đấy, nó sẽ kiểm tra bất kì đường dẫn khác mà bạn
đã mô tả ở phần cài đặt IDE. Nếu thất bại, nó sẽ quay trở lại tìm ở đường dẫn hệ
thống.
Nguyên tắc: Dùng dấu
ngoặc nhọn để include file header đến từ trình biên dịch. Sử dụng dấu nháy kép
để include bất kì header file khác.
Tại sao file iostream lại không có đuôi .h?
Một câu hỏi phổ biến
khác là “Tại sao iostream lại không (hoặc bất cứ file header của thư viện chuẩn
khác) có đuôi .h? Câu trả lời là, bởi vì iostream.h thì khác với file header
iostream! Để giải thích cần phải hiểu về một chút lịch sử.
Khi C++ lần đầu tiên
được tạo ra, tất cả các file trong thư viện runtime đều có đuôi .h. Cuộc sống vốn
kiên địnhm và nó sử dụng tốt. Phiên bản gốc của cout và cin nằm trong
iostream.h. Khi ngôn ngữ được chuẩn hóa bởi ủy ban ANSI. Họ quyết định chuyển tất
cả hàm trong thư viện runtime sang std namespace (đó là một ý tưởng tốt) Tuy
nhiên, nó sẽ xảy ra vấn đề: Nếu họ chuyển tất cả các hàm sáng std namespace,
không một chương trình cũ hơn nào có thể chạy được!
Để cố gắng giải quyết
vấn đề này và cung cấp tính tương thích của chương trình cũ, một tập hợp các
header file mới được giới thiệu mà sử dụng cùng tên nhưng thiếu mất đuôi .h. Những
header mới có tất cả các tính năng của nó trong std namespace. Theo cách này,
chương trình cũ có include #include <iostream.h> không cần phải viết lại,
và chương trình mới hơn có thể sử dụng #include <iostream>.
Khi bạn include một
header file từ thư viện chuẩn, chắc chắn rằng bạn sử dụng phiên bản không có
đuôi .h. Nếu không thì bạn sẽ sử dụng một phiên bản cũ mà không còn hỗ trợ nữa.
Chú ý: Nhiều header
trong thư viện chuẩn chỉ có phiên bản .h. Đối với những file này, thì việc include
là được chấp thuận. Nhiều thư viện này thì tương thích với chuẩn C, và C không
hỗ trợ namespace. Khi bạn tự viết header file, bạn nên thêm vào tất cả đuôi .h,
bởi vì bạn sẽ không đặt code của bạn trong std namespace.
Nguyên tắc: Sử dụng
phiên bản không có đuôi .h của thư viện nếu tồn tại, và truy cập các hàm thông
qua std namspace. Nếu phiên bản không đuôi .h không tồn tại, hoặc bạn tự tạo
header, hãy sử dụng phiên bản có đuôi .h.
Include header file từ đường dẫn khác
Một câu hỏi phổ biến
khác là làm sao để include header file từ đường dẫn khác.
Một cách để thực hiện
đó là:
2 |
#include "headers/myHeader.h"
#include "../moreHeaders/myOtherHeader.h"
|
Tác hại của cách tiếp
cận này đó là yêu cầu bạn phải đưa đường dẫn vào trong code. Nếu bạn cập nhật cấu
trúc đường dẫn, code của bạn sẽ không hoạt động.
Một phương pháp tốt
hơn là nói cho trình biên dịch hoặc IDE rằng bạn có một nhóm file header trong
một vài vị trí khác, vì thế nó sẽ tìm ở đó khi nó không tìm thấy tại đường dẫn
hiện tại. Điều này có thể thực hiện bằng cách cài đặt include path hoặc search
directory trong IDE cài đặt project.
Trong Visual Studio,
bạn có thể phải chuột vào project trong Solution Explorer, và chọn “properties”,
sau đó chọn tab “VC++ Directories”. Từ đây, bạn sẽ nhìn thấy một dòng gọi là “Include
directories”. Thêm đường dẫn include vào đây.
Trong Code::Blocks,
chọn Project menu và chọn “Build Options”, sau đó “Trong đường dẫn Search” tab.
Thêm đường dẫn include ở đây.
Sử dụng g++, bạn có
thể dùng –I option để đặc tả một đường dẫn include
1
|
g++ -o main -I /source/includes main.cpp
|
Ưu điểm của cách tiếp
cận này đó là nếu bạn có thay đổi cấu trúc đường dẫn, bạn chỉ cần thay đổi trên
trình biên dịch hoặc IDE thay vì mọi file code.
Tôi có thể đặt định nghĩa của hàm trong file
header được không?
C++ không báo lỗi nếu
bạn làm vậy, nhưng thông thường mà nói, bạn không nên.
Như đã đề cập ở trên,
khi bạn #include một file, toàn bộ nội dung của file của file được include sẽ
chèn vào điểm include. Điều này nghĩa là bất kì định nghĩa bạn đặt vào header đều
copied vào mọi file bạn include header đó.
Cho project nhỏ, điều
này không tạo ra nhiều vấn đề. Nhưng trong project lớn, điều này có thể làm cho
nó compile chậm hơn và có thể tốn dung lượng hơn nếu thực thi. Nếu bạn thay đổi
định nghĩa trong code file chỉ cần file .cpp đó biên dịch lại. Nếu bạn thay đổi
định nghĩa trong tất cả header file, mọi code file nào include header file đó đều
phải biên dịch lại. Một thay đổi nhỏ có thể gây cho bạn compile lại toàn bộ
project!
Thỉnh thoảng có ngoại
lệ xảy ra.( ví dụ nhưng hàm chỉ định nghĩa một dòng)
Header file thực tập tốt nhất
Đây là một vài cách
luyện tập tốt nhất để tự tạo header file cho bạn.
Luôn luôn bao gồm
header guards
Không định nghĩa biến
trong header file trừ khi chúng là hằng số. Header file nên thông thường chỉ
dùng để khai báo
Không định nghĩa hàm
trong header files.
Mỗi file header nên
có một công việc cụ thể, và càng độc lập càng tốt. Ví dụ, bạn có thể đặt tất cả
khai báo của bạn liên quan đến hàm A trong A.h và tất cả khai báo liên quan đến
hàm B trong B.h. Cách đó nếu bạn chỉ quan tâm đến A sau đó, bạn chỉ cần include
A.h và không cần gì liên quan đến B
Tên file header nên
trùng tên với file mã nguồn
Cố gắng giảm thiểu số
lượng của header khác bạn include trong header file của bạn. Chỉ include những
gì cần thiết.
Không include file
.cpp file
Nguồn: learncpp.com
Nhận xét
Đăng nhận xét