Chuyển đến nội dung chính Chia sẻ
tháng 12 17, 2015 [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 Chia sẻ
Nhận xét
Bài đăng phổ biến
tháng 12 15, 2015 [Học lập trình C++] Chương 1: 1.2 Comments
Chia sẻ Unknown Truy cập hồ sơ
Lưu trữ
- 2015 24
- tháng 12 18
- [Học lập trình C++] Chương 1: 1.11a – Debugging ch...
- [Học lập trình C++] Chương 1: 1.11 – Debugging chư...
- [Học lập trình C++] Chương 1: 1.10a – Làm thế nào ...
- [Học lập trình C++] Chương 1: 1.10 – làm quen với ...
- [Học lập trình C++] Chương 1: 1.9 – Header file
- [Học lập trình C++] Chương 1: 1.8 – Lập trình với ...
- [Học lập trình C++] Chương 1: 1.7 – tiền khai báo
- [Học lập trình C++] Chương 1: 1.6 – Khoảng trắng v...
- [Học lập trình C++] Chương 1: 1.5 – làm quen với t...
- [Học lập trình C++] Chương 1: 1.4d – Keywords và c...
- [Học lập trình C++] Chương 1: 1.4c – Làm quen với ...
- [Học lập trình C++] Chương 1: 1.4b Tại sao hàm lại...
- [Học lập trình C++] Chương 1: 1.4a Làm quen với th...
- [Học lập trình C++] Chương 1: 1.4 Làm quen với hàm...
- [Học lập trình C++] Chương 1: 1.3a Làm quen với co...
- [Học lập trình C++] Chương 1: 1.3 Làm quen với biế...
- [Học lập trình C++] Chương 1: 1.2 Comments
- [Học lập trình C++] Chương 1: 1.1 Cấu trúc của một...
- tháng 10 6
Hiện thêm Ẩn bớt Báo cáo vi phạm