[Khóa học C++] Bài 17 - Header guards

Share:

The duplicate definition problem

Trong bài 12 - Forward declarations and definitions, một identifier chỉ có thể có một definition. Vì vậy chương trình sau đây sẽ gây ra lỗi khi compile.


Tương tự, chương trình sau đây cũng gây ra lỗi khi compile (identifier foo() có tới 2 definition):

Như bạn thấy, hai chương trình trên cực kì dễ sửa lỗi, chúng ta chỉ cần xóa những definition dư thừa.

Bây giờ chúng ta gác lại hai ví dụ này, chúng ta cùng đi tới ví dụ trên header file mà bạn đã được học trong bài 15.


Đây là ví dụ mà một newbie sẽ thường xuyên gặp khi sử dụng header file. Bởi lẽ, chương trình này sẽ gây ra một lỗi khi compile. Chúng ta cùng tìm hiểu xem vì sao chúng ta gặp lỗi.

Đầu tiên, bạn hãy nhìn vào file main.cpp, dòng thứ 2 include file math.h, vì vậy toàn bộ nội dung file math.h sẽ được thêm vào tại vị trí dòng số 2 này. Tiếp theo ngay tại dòng số 3, file geometry.h được include, bởi vì trong file này lại include math.h, cuối cùng thì toàn bộ nội dung file math.h sẽ được thêm vào vị trí dòng số 3 này.
Vì thế hàm getSquareSides() có tới 2 definition (nó được define 2 lần). Và chương trình sẽ gây ra lỗi như 2 ví dụ đầu bài.

Sau khi preprocessor chạy xong, file main.cpp sẽ như sau:

Đây là một lỗi rất thường xuyên khi bạn chưa biết cách sử dụng header file, một kĩ thuật khi đã được giới thiệu trong bài header file là header guard, giúp tránh tình trạng một header file được include 2 lần trong 1 file (như trong trường hợp này file math.h được include 2 lần trong file main.cpp).

Header guards

Header guard (hay còn gọi là inlcude guard) là một phương pháp cực kì đơn giản để tránh việc include header file 2 lần trong một file source.

Sau đây là cú pháp để chúng ta sử dụng header guard:

Chúng ta tạm gọi file này là guard.h, khi file này được include trong một file nào đó, giả sử là file main.cpp, SOME_UNIQUE_NAME_HERE sẽ được kiểm tra xem đã được define hay chưa, nếu chưa được define (đây là lần đầu file guard.h được include trong main.cpp), thì SOME_UNIQUE_NAME_HERE sẽ được define và nội dung của header file sẽ được copy vào file main.cpp.
Nếu bạn chỉ include guard.h một lần trong main.cpp, thì không có chuyện gì xảy ra. Nhưng nếu bạn include file này một lần nữa trong main.cpp, SOME_UNIQUE_NAME_HERE sẽ tiếp tục được kiểm tra xem đã được define trước đó hay chưa. Và chính xác là nó đã được define trước đó khi include guard.h lần thứ nhất. Kết quả là toàn bộ nội dung header file sẽ được bỏ qua. Lỗi duplicate definition sẽ không xảy ra.

Bạn có thể đặt tên cho macro này một cách tùy ý theo ý muốn của bạn SOME_UNIQUE_NAME_HERE, nhưng chúng tôi khuyên bạn nên đặt tên trùng với file name. Ví dụ header file là math.h thì macro nên là MATH_H.

Hay thậm chí nếu bạn xem file iostream trong Visual Studio, bởi vì những thư viện chuẩn (standard library) cũng sử dụng header guard.

Updating our previous example with header guards

Quay lại ví dụ ở đầu bài, chúng ta sẽ update lại các file và sử dụng header guard.


Bạn hãy thử phân tích ví dụ này để hiểu rõ về header guard. Và chắc chắn ví dụ này không báo lỗi.

#pragma once

Hiện nay rất nhiều compiler hổ trợ #pragma once, và nó có mục đích tương tự như header guard.

Tuy nhiên, #pragma once không phải là thành phần chính thức trong C++, không phải compiler nào cũng hổ trợ.
Và đương nhiên, chúng tôi khuyên bạn sử dụng header guard thay vì #pragma once.



Không có nhận xét nào