Đảm bảo rằng các tiêu đề được bao gồm rõ ràng trong tệp CPP


9

Tôi nghĩ rằng thông thường tốt cho #includetiêu đề cho bất kỳ loại nào được sử dụng trong tệp CPP, bất kể những gì đã được bao gồm thông qua tệp HPP. Vì vậy, tôi có thể #include <string>trong cả HPP và CPP của mình, ví dụ, mặc dù tôi vẫn có thể biên dịch nếu tôi bỏ qua nó trong CPP. Bằng cách này, tôi không phải lo lắng về việc HPP của mình có sử dụng khai báo chuyển tiếp hay không.

Có công cụ nào có thể thực thi #includekiểu mã hóa này không? Tôi có nên thực thi kiểu mã hóa này không?

Vì bộ tiền xử lý / trình biên dịch không quan tâm #includeđến việc đến từ HPP hay CPP, tôi không nhận được phản hồi nếu tôi quên làm theo kiểu này.

Câu trả lời:


7

Đây là một trong những loại "nên" chứ không phải "sẽ" các loại tiêu chuẩn mã hóa. Lý do là bạn sẽ phải viết một trình phân tích cú pháp C ++ để thực thi nó.

Một quy tắc rất phổ biến cho các tệp tiêu đề là chúng phải tự đứng. Tệp tiêu đề không được yêu cầu một số tệp tiêu đề khác phải được loại trừ trước khi bao gồm tiêu đề được đề cập. Đây là một yêu cầu thử nghiệm. Đưa ra một số tiêu đề ngẫu nhiên foo.hh, sau đây sẽ biên dịch và chạy:

#include "foo.hh"
int main () {
   return 0;
}

Quy tắc này có hậu quả liên quan đến việc sử dụng các lớp khác trong một số tiêu đề. Đôi khi những hậu quả có thể tránh được bằng cách tuyên bố chuyển tiếp các lớp khác. Điều này là không thể với rất nhiều lớp thư viện tiêu chuẩn. Không có cách nào để chuyển tiếp khai báo một khởi tạo mẫu như std::string, hoặc std::vector<SomeType>. Bạn phải sử dụng #includecác tiêu đề STL đó trong tiêu đề ngay cả khi việc sử dụng loại duy nhất là làm đối số cho hàm.

Một vấn đề khác là với những thứ mà bạn tình cờ kéo vào. Ví dụ: xem xét các vấn đề sau:

tập tin foo.cc:

#include "foo.hh"
#include "bar.hh"

void Foo::Foo () : bar() { /* body elided */ }

void Foo::do_something (int item) {
   ...
   bar.add_item (item);
   ...
}

Đây barlà một Foothành viên dữ liệu lớp là loại Bar. Bạn đã thực hiện đúng ở đây và có #included bar.hh mặc dù điều đó sẽ phải được bao gồm trong tiêu đề xác định lớp Foo. Tuy nhiên, bạn chưa bao gồm những thứ được sử dụng bởi Bar::Bar()Bar::add_item(int). Có nhiều trường hợp các cuộc gọi này có thể dẫn đến các tham chiếu bên ngoài bổ sung.

Nếu bạn phân tích foo.obằng một công cụ như nm, có vẻ như các hàm trong foo.ccđang gọi tất cả các loại công cụ mà bạn chưa thực hiện phù hợp #include. Vì vậy, bạn nên thêm #includechỉ thị cho những tài liệu tham khảo bên ngoài ngẫu nhiên foo.cc? Câu trả lời dĩ nhiên là không rồi. Vấn đề là rất khó để phân biệt các chức năng được gọi là tình cờ với các chức năng được gọi trực tiếp.


2

Tôi có nên thực thi kiểu mã hóa này không?

Chắc là không. Quy tắc của tôi là thế này: bao gồm tệp tiêu đề không thể phụ thuộc vào thứ tự.

Bạn có thể xác minh điều này khá dễ dàng với quy tắc đơn giản rằng tệp đầu tiên được bao gồm bởi xc là xh


1
bạn có thể xây dựng trên này? Tôi không thể thấy điều đó thực sự sẽ xác minh sự độc lập trật tự.
gièm pha

1
nó không thực sự đảm bảo tính độc lập của đơn hàng - nó chỉ đảm bảo rằng nó #include "x.h"sẽ hoạt động mà không yêu cầu một số điều trước đó #include. Điều đó đủ tốt nếu bạn không lạm dụng #define.
kevin cline

1
Ồ tôi hiểu rồi. Vẫn là một ý tưởng tốt.
gièm pha

2

Nếu bạn cần thực thi một quy tắc mà các tệp tiêu đề cụ thể phải tự đứng vững, bạn có thể sử dụng các công cụ bạn đã có. Tạo tệp tạo tệp cơ bản biên dịch từng tệp tiêu đề riêng lẻ nhưng không tạo tệp đối tượng. Bạn sẽ có thể chỉ định chế độ nào để biên dịch tệp tiêu đề trong (chế độ C hoặc C ++) và xác minh rằng nó có thể tự đứng vững. Bạn có thể đưa ra giả định hợp lý rằng đầu ra không chứa bất kỳ dương tính giả nào, tất cả các phụ thuộc bắt buộc được khai báo và đầu ra là chính xác.

Nếu bạn đang sử dụng IDE, bạn vẫn có thể thực hiện việc này mà không cần tệp Makefile (tùy thuộc vào IDE của bạn). Chỉ cần tạo một dự án bổ sung, thêm các tệp tiêu đề bạn muốn xác minh và thay đổi cài đặt để biên dịch nó thành tệp C hoặc C ++. Ví dụ, trong MSVC, bạn sẽ thay đổi cài đặt "Loại mục" trong "Thuộc tính cấu hình-> Chung".


1

Tôi không nghĩ rằng một công cụ như vậy tồn tại, nhưng tôi sẽ rất vui nếu một số câu trả lời khác từ chối tôi.

Vấn đề với việc viết một công cụ như vậy là nó rất dễ dàng báo cáo kết quả sai, vì vậy tôi ước tính lợi thế ròng của một công cụ như vậy gần bằng không.

Cách duy nhất để một công cụ như vậy có thể hoạt động là nếu nó có thể đặt lại bảng ký hiệu của nó thành chỉ nội dung của tệp tiêu đề mà nó đã xử lý, nhưng sau đó bạn gặp phải vấn đề mà các tiêu đề hình thành API bên ngoài của thư viện ủy quyền khai báo thực tế tiêu đề nội bộ.
Ví dụ, <string>trong triển khai libc ++ của GCC không khai báo bất cứ điều gì, nhưng nó chỉ bao gồm một loạt các tiêu đề nội bộ có chứa các khai báo thực tế. Nếu công cụ đặt lại bảng biểu tượng của nó thành chỉ những gì được khai báo bởi <string>chính nó, thì đó sẽ không là gì cả.
Bạn có thể có công cụ phân biệt giữa #include ""#include <>, nhưng điều đó không giúp ích gì cho bạn nếu thư viện bên ngoài sử dụng #include ""để bao gồm các tiêu đề nội bộ của nó trong API.


1

không #Pragma onceđạt được điều này? Bạn có thể bao gồm một cái gì đó bao nhiêu lần tùy ý, bao gồm trực tiếp hoặc thông qua chuỗi bao gồm, và miễn là có một cái #Pragma oncebên cạnh chúng, tiêu đề chỉ được đưa vào một lần.

Để thực thi nó, có lẽ bạn có thể tạo một hệ thống xây dựng chỉ bao gồm mỗi tiêu đề với một số chức năng chính giả, chỉ để đảm bảo nó biên dịch. #ifdefchuỗi bao gồm cho kết quả tốt nhất với phương pháp thử nghiệm đó.


1

Luôn bao gồm các tệp tiêu đề trong tệp CPP. Điều này không chỉ rút ngắn thời gian biên dịch đáng kể mà còn giúp bạn tiết kiệm rất nhiều rắc rối nếu bạn quyết định sử dụng các tiêu đề được biên dịch trước. Theo kinh nghiệm của tôi, thậm chí làm những rắc rối của khai báo chuyển tiếp là đáng giá trong khi thực hành. Phá vỡ quy tắc chỉ khi cần thiết.


Bạn có thể giải thích làm thế nào điều này sẽ "rút ngắn thời gian biên dịch đáng kể"?
Mawg nói rằng phục hồi Monica

1
Điều này đảm bảo rằng mỗi đơn vị biên dịch (tệp CPP) chỉ kéo tối thiểu các tệp bao gồm thời gian biên dịch. Mặt khác, nếu bạn đặt bao gồm trong các tệp H, chuỗi phụ thuộc sai sẽ hình thành nhanh chóng và cuối cùng bạn sẽ kéo tất cả bao gồm với mỗi phần biên dịch.
Gvozden

Với những người bảo vệ, tôi có thể bỏ qua "ýanbtly", nhưng tôi cho rằng việc truy cập đĩa (theo thứ tự để khám phá những thước đo inlcude) là "chậm" vì vậy sẽ nhượng lại điểm ,. Cảm ơn đã làm rõ
Mawg nói rằng phục hồi lại

0

Tôi muốn nói có cả ưu điểm và nhược điểm của quy ước này. Một mặt, thật tuyệt khi biết chính xác tệp .cpp của bạn bao gồm những gì. Mặt khác, danh sách bao gồm có thể dễ dàng phát triển đến một kích thước vô lý.

Một cách để khuyến khích quy ước này là không bao gồm bất cứ điều gì trong tiêu đề của riêng bạn, mà chỉ trong các tệp .cpp. Sau đó, bất kỳ tệp .cpp nào sử dụng tiêu đề của bạn sẽ không biên dịch trừ khi bạn bao gồm rõ ràng tất cả các tiêu đề khác mà nó phụ thuộc.

Có lẽ một số thỏa hiệp hợp lý là theo thứ tự ở đây. Ví dụ: bạn có thể quyết định có thể bao gồm các tiêu đề thư viện tiêu chuẩn bên trong các tiêu đề của riêng bạn, nhưng không còn nữa.


3
Nếu bạn rời khỏi bao gồm các tiêu đề, thì bao gồm thứ tự bắt đầu có vấn đề ... và tôi chắc chắn muốn tránh điều đó.
M. Dudley

1
-1: Tạo cơn ác mộng phụ thuộc. Việc cải tiến xh có thể yêu cầu thay đổi tệp MERYI .cpp bao gồm xh
kevin cline

1
@ M.Dudley, Như tôi đã nói, có những lợi thế và bất lợi.
Dima
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.