Thứ tự nào nên bao gồm các tệp được chỉ định, nghĩa là những lý do để bao gồm một tiêu đề trước một tiêu đề khác là gì?
Ví dụ: các tệp hệ thống, STL và Boost đi trước hoặc sau tệp cục bộ bao gồm các tệp?
Thứ tự nào nên bao gồm các tệp được chỉ định, nghĩa là những lý do để bao gồm một tiêu đề trước một tiêu đề khác là gì?
Ví dụ: các tệp hệ thống, STL và Boost đi trước hoặc sau tệp cục bộ bao gồm các tệp?
Câu trả lời:
Tôi không nghĩ rằng có một thứ tự được đề nghị, miễn là nó được biên dịch! Điều khó chịu là khi một số tiêu đề yêu cầu các tiêu đề khác được đưa vào trước ... Đó là vấn đề với chính các tiêu đề, không phải theo thứ tự bao gồm.
Sở thích cá nhân của tôi là đi từ địa phương đến toàn cầu, mỗi tiểu mục theo thứ tự bảng chữ cái, nghĩa là:
Lý do của tôi cho 1. là nó phải chứng minh rằng mỗi tiêu đề (trong đó có một cpp) có thể là #include
d mà không cần điều kiện tiên quyết (terminus technicus: tiêu đề là "khép kín"). Và phần còn lại dường như chảy một cách hợp lý từ đó.
Điều quan trọng cần ghi nhớ là các tiêu đề của bạn không nên phụ thuộc vào các tiêu đề khác được đưa vào trước. Một cách để đảm bảo điều này là bao gồm các tiêu đề của bạn trước bất kỳ tiêu đề nào khác.
"Suy nghĩ trong C ++" đặc biệt đề cập đến điều này, tham khảo "Thiết kế phần mềm C ++ quy mô lớn" của Lakos:
Có thể tránh được các lỗi sử dụng tiềm ẩn bằng cách đảm bảo rằng tệp .h của một thành phần phân tích cú pháp - không có các khai báo hoặc định nghĩa được cung cấp bên ngoài ... Bao gồm tệp .h là dòng đầu tiên của tệp .c đảm bảo rằng không có phần quan trọng nào thông tin nội tại giao diện vật lý của thành phần bị thiếu trong tệp .h (hoặc, nếu có, bạn sẽ tìm hiểu về nó ngay khi bạn cố gắng biên dịch tệp .c).
Điều đó có nghĩa là, bao gồm theo thứ tự sau:
Nếu bất kỳ tiêu đề nào có vấn đề với việc được đưa vào thứ tự này, hãy sửa chúng (nếu là của bạn) hoặc không sử dụng chúng. Thư viện tẩy chay không viết tiêu đề sạch.
Hướng dẫn về phong cách C ++ của Google lập luận gần như ngược lại, thực sự không có lý do nào cả; Cá nhân tôi có xu hướng ủng hộ phương pháp Lakos.
Tôi tuân theo hai quy tắc đơn giản để tránh phần lớn các vấn đề:
Tôi cũng làm theo hướng dẫn của:
Nói cách khác:
#include <stdio.h>
#include <string.h>
#include "btree.h"
#include "collect_hash.h"
#include "collect_arraylist.h"
#include "globals.h"
Mặc dù, là hướng dẫn, đó là một điều chủ quan. Mặt khác, các quy tắc, tôi thực thi một cách cứng nhắc, thậm chí đến mức cung cấp các tệp tiêu đề 'bao bọc' bao gồm các vệ sĩ và được nhóm lại bao gồm nếu một số nhà phát triển bên thứ ba đáng ghét không đăng ký vào tầm nhìn của tôi :-)
Để thêm gạch của riêng tôi vào tường.
Vì vậy, tôi thường đi như thế này:
// myproject/src/example.cpp
#include "myproject/example.h"
#include <algorithm>
#include <set>
#include <vector>
#include <3rdparty/foo.h>
#include <3rdparty/bar.h>
#include "myproject/another.h"
#include "myproject/specific/bla.h"
#include "detail/impl.h"
Mỗi nhóm được phân tách bằng một dòng trống từ nhóm tiếp theo:
Cũng lưu ý rằng, ngoài các tiêu đề hệ thống, mỗi tệp nằm trong một thư mục có tên không gian tên của nó, chỉ vì việc theo dõi chúng theo cách này dễ dàng hơn.
#define
làm rối mã khác) và để ngăn chặn các phụ thuộc ngầm. Ví dụ: nếu tệp tiêu đề cơ sở mã của chúng tôi foo.h
thực sự phụ thuộc vào <map>
nhưng ở mọi nơi nó được sử dụng trong .cc
các tệp, <map>
tình cờ đã được đưa vào, có lẽ chúng tôi sẽ không nhận thấy. Cho đến khi ai đó cố gắng bao gồm foo.h
mà không bao gồm đầu tiên <map>
. Và rồi họ sẽ bực mình.
.h
có ít nhất một .cpp
vấn đề bao gồm nó trước (thực sự, trong mã cá nhân của tôi, bài kiểm tra Đơn vị liên quan bao gồm nó trước và mã nguồn bao gồm nó trong nhóm hợp pháp của nó ). Về việc không bị ảnh hưởng, nếu bất kỳ tiêu đề nào bao gồm <map>
thì tất cả các tiêu đề bao gồm sau đó đều bị ảnh hưởng, vì vậy có vẻ như là một trận thua với tôi.
Header corresponding to this cpp file first (sanity check)
. Có điều gì đặc biệt nếu #include "myproject/example.h"
được chuyển đến cuối của tất cả bao gồm?
Tôi đề nghị:
Và tất nhiên, thứ tự chữ cái trong mỗi phần, nếu có thể.
Luôn sử dụng khai báo chuyển tiếp để tránh #include
s không cần thiết trong tệp tiêu đề của bạn.
Tôi khá chắc chắn rằng đây không phải là một thực tiễn được đề xuất ở bất cứ đâu trong thế giới lành mạnh, nhưng tôi muốn hệ thống dòng bao gồm theo chiều dài tên tệp, được sắp xếp theo từ vựng trong cùng một độ dài. Thích như vậy:
#include <set>
#include <vector>
#include <algorithm>
#include <functional>
Tôi nghĩ rằng nên bao gồm các tiêu đề của riêng bạn trước các dân tộc khác, để tránh sự xấu hổ của sự phụ thuộc theo thứ tự bao gồm.
windows.h
.
Đây không phải là chủ quan. Hãy chắc chắn rằng các tiêu đề của bạn không dựa vào việc được #include
theo thứ tự cụ thể. Bạn có thể chắc chắn rằng việc bạn bao gồm các tiêu đề STL hoặc Boost không quan trọng.
Đầu tiên bao gồm tiêu đề tương ứng với .cpp ... nói cách khác, source1.cpp
nên bao gồm source1.h
trước khi bao gồm mọi thứ khác. Ngoại lệ duy nhất tôi có thể nghĩ đến là khi sử dụng MSVC với các tiêu đề được biên dịch sẵn trong trường hợp đó, bạn buộc phải đưa vào stdafx.h
trước mọi thứ khác.
Lý do: Bao gồm source1.h
trước bất kỳ tệp nào khác đảm bảo rằng nó có thể độc lập mà không phụ thuộc vào nó. Nếu source1.h
mất một phụ thuộc vào một ngày sau đó, trình biên dịch sẽ ngay lập tức cảnh báo bạn để thêm các khai báo chuyển tiếp cần thiết vào source1.h
. Điều này lần lượt đảm bảo rằng các tiêu đề có thể được bao gồm trong bất kỳ thứ tự nào bởi những người phụ thuộc của họ.
Thí dụ:
nguồn1.h
class Class1 {
Class2 c2; // a dependency which has not been forward declared
};
nguồn1.cpp
#include "source1.h" // now compiler will alert you saying that Class2 is undefined
// so you can forward declare Class2 within source1.h
...
Người dùng MSVC: Tôi thực sự khuyên bạn nên sử dụng các tiêu đề được biên dịch trước. Vì vậy, hãy chuyển tất cả các #include
chỉ thị cho các tiêu đề tiêu chuẩn (và các tiêu đề khác sẽ không bao giờ thay đổi) sang stdafx.h
.
Bao gồm từ cụ thể nhất đến ít cụ thể nhất, bắt đầu bằng .hpp tương ứng cho .cpp, nếu một trong số đó tồn tại. Bằng cách đó, mọi phụ thuộc ẩn trong các tệp tiêu đề không tự cung cấp sẽ được tiết lộ.
Điều này là phức tạp bởi việc sử dụng các tiêu đề được biên dịch trước. Một cách khác là, không làm cho trình biên dịch dự án của bạn cụ thể, là sử dụng một trong các tiêu đề dự án làm tiêu đề được biên dịch trước bao gồm tệp.
Đó là một câu hỏi khó trong thế giới C / C ++, với rất nhiều yếu tố vượt quá tiêu chuẩn.
Tôi nghĩ rằng thứ tự tập tin tiêu đề không phải là một vấn đề nghiêm trọng miễn là nó biên dịch, như squelart nói.
Ý tưởng của tôi là: Nếu không có xung đột biểu tượng trong tất cả các tiêu đề đó, mọi thứ tự đều ổn và vấn đề phụ thuộc tiêu đề có thể được khắc phục sau đó bằng cách thêm các dòng #incoide vào .h.
Rắc rối thực sự phát sinh khi một số tiêu đề thay đổi hành động của nó (bằng cách kiểm tra các điều kiện #if) theo các tiêu đề ở trên.
Ví dụ: trong stddef.h trong VS2005, có:
#ifdef _WIN64
#define offsetof(s,m) (size_t)( (ptrdiff_t)&(((s *)0)->m) )
#else
#define offsetof(s,m) (size_t)&(((s *)0)->m)
#endif
Bây giờ vấn đề: Nếu tôi có một tiêu đề tùy chỉnh ("custom.h") cần được sử dụng với nhiều trình biên dịch, bao gồm một số trình biên dịch cũ không cung cấp offsetof
trong tiêu đề hệ thống của họ, tôi nên viết tiêu đề của mình:
#ifndef offsetof
#define offsetof(s,m) (size_t)&(((s *)0)->m)
#endif
Và hãy chắc chắn thông báo cho người dùng #include "custom.h"
sau tất cả các tiêu đề hệ thống, nếu không, dòng offsetof
trong stddef.h sẽ xác nhận lỗi xác định lại macro.
Chúng tôi cầu nguyện không gặp bất kỳ trường hợp như vậy trong sự nghiệp của chúng tôi.