2009-03-03 30 views
9

Mỗi tệp .C hoặc .cpp phải có tệp tiêu đề (.h) không?Mỗi tệp C hoặc C++ có tệp tiêu đề được liên kết không?

Giả sử có đang theo dõi các file C:

  1. main.c

  2. Func1.C

  3. Func2.C

  4. Func3.C

trong đó main() nằm trong tệp Main.C. Nếu có bốn tập tin tiêu đề

  1. Main.h

  2. Func1.h

  3. Func2.h

  4. Func3.h

Hoặc có nên chỉ có một tệp tiêu đề cho tất cả các tệp .C?

Phương pháp tiếp cận tốt hơn là gì?

Trả lời

3

Thường thì sẽ có một tệp .h cho mỗi tệp .c/.cpp.

4

Nói chung tốt nhất là nên có tệp tiêu đề cho mỗi tệp .c, chứa các khai báo cho các hàm vv trong tệp .c mà bạn muốn hiển thị. Bằng cách đó, một tệp .c khác có thể bao gồm tệp .h cho các chức năng cần thiết và sẽ không cần được biên dịch lại nếu tệp tiêu đề không được bao gồm đã thay đổi.

2

Điều đó tùy thuộc. Thông thường lý do của bạn để có các tệp .c riêng biệt sẽ quyết định xem bạn có cần tệp .h riêng biệt hay không.

28

Thật không bình thường khi có một số main.h vì thường không có gì cần phải được tiếp xúc với các đơn vị biên dịch khác tại thời gian biên dịch. main() chính nó cần phải được tiếp xúc cho các mối liên kết/bắt đầu-up mã nhưng họ không sử dụng các tập tin tiêu đề.

Bạn có thể có một tệp tiêu đề cho mỗi tệp C hoặc rất có thể là tệp tiêu đề cho một nhóm tệp C có liên quan.

Một ví dụ về điều đó là nếu bạn có triển khai BTree và bạn đã thêm, xóa, tìm kiếm, v.v. trong các tệp C của chính chúng để giảm thiểu biên dịch lại khi mã thay đổi. Nó không có ý nghĩa trong trường hợp đó để có các tệp tiêu đề riêng biệt cho mỗi tệp C khi tiêu đề là API, chế độ xem của thư viện cho người dùng. Tôi nghi ngờ người dùng sẽ muốn bao gồm 6 tập tin tiêu đề chỉ để có thể sử dụng các chức năng. Sẽ có một tệp btree.h và một tệp btree.lib chứa tất cả các đối tượng BTree được tạo từ các tệp C riêng lẻ.

Ví dụ khác có thể được tìm thấy trong tiêu đề C chuẩn.Chúng tôi không biết chắc chắn liệu có nhiều tệp C cho các hàm stdio.h (đó là cách tôi làm nhưng đó không phải là cách duy nhất) nhưng, ngay cả khi có, chúng được coi là đơn vị về mặt API. Bạn không cần phải bao gồm stdio_printf.h, stdio_fgets.h v.v. - có một đơn stdio.h cho phần I/O chuẩn của thư viện thời gian chạy C.

+1

Không +1 từ tôi vì bạn không đề cập đến mẫu: ( – Marcin

+0

Tôi tập trung nhiều hơn ở phía C, nhưng tại sao tôi lại đề cập đến các mẫu, ngay cả đối với C++? Tôi không thấy khả năng ứng dụng của chúng đối với các tệp tiêu đề được sử dụng (chúng có thể đi vào các tệp tiêu đề nhưng có thể typedefs và tôi không đề cập đến chúng) một cách rõ ràng) – paxdiablo

1

Không có cách tiếp cận nào tốt hơn, chỉ các trường hợp phổ biến và ít phổ biến hơn.

Trường hợp phổ biến hơn là khi bạn có giao diện lớp/chức năng để khai báo/xác định. Tốt hơn là chỉ nên có một tệp .cpp/.c với các định nghĩa và một tiêu đề cho các khai báo. Cung cấp cho họ cùng tên dễ hiểu rằng chúng có liên quan trực tiếp.

Nhưng đó không phải là "quy tắc", đó là cách thông thường và hiệu quả nhất trong hầu hết các trường hợp.

Hiện tại trong một số trường hợp (như các lớp mẫu hoặc một số định nghĩa cấu trúc nhỏ), bạn sẽ không cần bất kỳ tệp .c/.cpp nào, chỉ cần tiêu đề. Chúng tôi thường có một số định nghĩa giao diện lớp ảo chỉ trong một tệp tiêu đề chẳng hạn, chỉ với các hàm thuần túy ảo hoặc các hàm tầm thường.

Và trong các trường hợp hiếm gặp khác (như tệp main.c/.cpp giả định) nếu không phải lúc nào cũng được yêu cầu cho phép mã từ đơn vị biên dịch bên ngoài gọi hàm của một đơn vị biên dịch đã cho. Chức năng chính là một ví dụ (không có tiêu đề/khai báo cần thiết), nhưng có những người khác, chủ yếu là khi mã của nó "kết nối tất cả các phần khác với nhau" và không được gọi bởi các phần khác của ứng dụng. Điều đó rất hiếm nhưng trong trường hợp này một tiêu đề không có ý nghĩa.

0

Như đã lưu ý, thông thường, sẽ có một tệp tiêu đề (.h) cho mỗi tệp (.c hoặc .cpp).

Tuy nhiên, bạn nên xem xét tính gắn kết của các tệp. Nếu các tệp nguồn khác nhau cung cấp các bộ chức năng riêng biệt, có thể sử dụng lại được - một tổ chức lý tưởng - thì bạn chắc chắn có một tiêu đề cho mỗi tệp. Tuy nhiên, nếu ba tệp nguồn cung cấp một tập hợp các hàm (đó là quá lớn để vừa với một tệp), thì bạn sẽ sử dụng một tổ chức phức tạp hơn. Sẽ có một tiêu đề cho các dịch vụ bên ngoài được chương trình chính sử dụng - và điều đó sẽ được sử dụng bởi các chương trình khác cần các dịch vụ tương tự. Cũng sẽ có một tiêu đề thứ hai được sử dụng bởi các tệp nguồn hợp tác cung cấp các định nghĩa 'nội bộ' được chia sẻ bởi các tệp đó.

(Cũng được ghi chú bởi Pax): Chương trình chính thường không cần tiêu đề của chính nó - không có mã nguồn nào khác nên sử dụng các dịch vụ mà nó cung cấp; nó sử dụng các dịch vụ được cung cấp bởi các tập tin khác.

0

Nếu bạn muốn mã được biên dịch của bạn được sử dụng từ một đơn vị biên dịch khác, bạn sẽ cần các tệp tiêu đề. Có một số tình huống mà bạn hiện cần/muốn có tiêu đề.

Trường hợp đầu tiên như vậy là các tệp main.c/cpp. Lớp này không có nghĩa là để được bao gồm và như vậy không có nhu cầu cho một tập tin tiêu đề.

Trong một số trường hợp, bạn có thể có tệp tiêu đề xác định hành vi của một tập hợp các triển khai khác nhau được tải qua dll được tải khi chạy. Sẽ có các tập tin .c/.cpp khác nhau thực hiện các biến thể của cùng một tiêu đề. Điều này có thể phổ biến trong các hệ thống plugin.

10
  1. Tệp tiêu đề là không bắt buộc.

  2. #include chỉ cần sao chép/dán bất kỳ tệp nào được bao gồm (bao gồm.Các tệp nguồn c)

  3. Thường được sử dụng trong các dự án thực tế là các tệp tiêu đề chung như config.hconstants.h có chứa thông tin thường được sử dụng như cờ biên dịch và hằng số dự án.

  4. Thiết kế tốt của API thư viện sẽ hiển thị giao diện chính thức với một bộ tệp tiêu đề và sử dụng tập hợp tệp tiêu đề nội bộ để triển khai cùng với tất cả chi tiết. Điều này cho biết thêm một lớp trừu tượng bổ sung tuyệt vời vào thư viện C mà không cần thêm bloat không cần thiết.

  5. Sử dụng ý thức chung. C/C++ không thực sự dành cho những người không có nó.

0

Thường là tệp cpp/c để triển khai và tệp h/hpp (hpp không được sử dụng thường xuyên) dành cho tệp tiêu đề (chỉ có nguyên mẫu và khai báo). Các tệp Cpp không phải lúc nào cũng phải có một tệp tiêu đề liên kết với nó nhưng nó thường hoạt động như tệp tiêu đề hoạt động như một cầu nối giữa các tệp cpp để mỗi tệp cpp có thể sử dụng mã từ một tệp cpp khác.

Một điều cần được thực thi mạnh mẽ là không sử dụng mã trong tệp tiêu đề! Đã có quá nhiều lần các tệp tiêu đề phá vỡ các biên dịch trong bất kỳ dự án kích thước nào do định nghĩa lại. Và đó chỉ đơn giản là khi bạn đưa tệp tiêu đề vào 2 tệp cpp khác nhau. Các tệp tiêu đề phải luôn được thiết kế để được bao gồm nhiều lần. Các tệp Cpp không bao giờ được bao gồm.

0

Nói chung, tôi không nghĩ có bất kỳ mối quan hệ rõ ràng nào giữa tệp .h và .c. Trong nhiều trường hợp (có thể là hầu hết), một đơn vị mã là một thư viện chức năng với một giao diện công cộng (.h) và một triển khai mờ (.c). Đôi khi một số biểu tượng là cần thiết, như enums hoặc macro và bạn nhận được a .h không tương ứng .c và trong một vài trường hợp, bạn sẽ có một khối mã không có giao diện công cộng và không tương ứng .h

đặc biệt, có một số lần, vì mục đích dễ đọc, các tiêu đề hoặc triển khai (hiếm khi cả hai) quá lớn và lông mà chúng sẽ bị chia thành nhiều tệp nhỏ hơn, vì lợi ích của lập trình viên.

2

Tôi thích đặt giao diện vào tệp tiêu đề và triển khai trong tệp cpp. Tôi không thích viết C++ nơi tôi cần phải thêm các biến thành viên và nguyên mẫu vào tiêu đề và sau đó phương thức một lần nữa trong C++. Tôi thích một cái gì đó như:

module.h

struct IModuleInterface : public IUnknown 
{ 
    virtual void SomeMethod() = 0; 
} 

module.cpp

class ModuleImpl : public IModuleInterface, 
        public CObject // a common object to do the reference 
               // counting stuff for IUnknown (so we 
               // can stick this object in a smart 
               // pointer). 
{ 
    ModuleImpl() : m_MemberVariable (0) 
    { 
    } 

    int m_MemberVariable; 

    void SomeInternalMethod() 
    { 
     // some internal code that doesn't need to be in the interface 
    } 

    void SometMethod() 
    { 
     // implementation for the method in the interface 
    } 

    // whatever else we need 
}; 

tôi thấy đây là một cách thực sự trong sạch của chia thực hiện và giao diện.

+0

Vì vậy, nó sẽ ném một lỗi nếu bạn không thực hiện SomeMethod() trong lớp kế thừa? – deworde

+0

Vâng, đó là triết lý. Tôi chắc chắn không thể lấy tín dụng cho cách tiếp cận này, một đồng khá rực rỡ công nhân đã đưa ra mô hình và tôi đã sử dụng nó kể từ đó. Đúng, bạn sẽ nhận được các lỗi biên dịch nếu bạn không triển khai đúng giao diện bên ngoài của mình. – RedBlueThing

+0

Điều này đã được sử dụng xuyên suốt Adobe InDesign SDK. Tôi tin rằng nó đến từ Microsoft COM. – Kugel

1

Đó là tất cả về mã nào cần phải biết mã nào khác. Bạn muốn giảm số lượng các tập tin khác nhận thức được đến mức tối thiểu cho họ để làm công việc của họ.

Họ cần biết rằng một hàm tồn tại, loại nào họ cần truyền vào nó và loại nào sẽ trả về, nhưng không phải là những gì nó làm trong nội bộ. Lưu ý rằng nó cũng quan trọng từ quan điểm của lập trình viên để biết những gì các loại thực sự có ý nghĩa. (ví dụ: int là hàng, và đó là cột) nhưng chính mã đó không quan tâm. Đây là lý do tại sao việc đặt tên hàm và tham số hợp lý là đáng giá.

Như những người khác đã nói, nếu không có gì trong tệp cpp có giá trị hiển thị với các phần khác của mã, như thường là trường hợp với main.c, thì không cần tệp tiêu đề. Nó đôi khi đáng để đặt mọi thứ bạn muốn phơi bày trong một tệp tiêu đề duy nhất (ví dụ, Func1and2and3.h), để bất cứ điều gì mà biết về Func1 cũng biết về Func2, nhưng cá nhân tôi không quan tâm đến điều này, như nó có nghĩa là bạn có xu hướng tải một đống rác cùng với những thứ bạn thực sự muốn.

Tóm tắt: Hãy tưởng tượng rằng bạn tin rằng ai đó có thể viết mã và thuật toán, thiết kế, v.v. đều tốt. Bạn muốn sử dụng mã họ đã viết. Tất cả những gì bạn cần biết là những gì để cung cấp cho họ để có được một cái gì đó để xảy ra, những gì bạn nên cung cấp cho nó, và những gì bạn sẽ nhận được trở lại. Đó là những gì cần phải đi trong các tập tin tiêu đề.

1

Nếu tệp của bạn hiển thị giao diện - tức là nếu tệp có chức năng sẽ được gọi từ các tệp khác - thì tệp sẽ có tệp tiêu đề. Nếu không, nó không nên.

6

Tôi thường theo xu hướng "nó phụ thuộc" cho đến khi tôi nhận ra tính nhất quán, đồng nhất và đơn giản quan trọng hơn tiết kiệm nỗ lực tạo tệp và "tiêu chuẩn tốt ngay cả khi chúng xấu".

Ý tôi là như sau: một cặp .cpp/.h là khá nhiều những gì mà tất cả "mô-đun" kết thúc. Làm cho cả hai yêu cầu này tiết kiệm rất nhiều sự nhầm lẫn và kỹ thuật xấu.

Ví dụ, khi tôi thấy một số giao diện của một cái gì đó trong một tập tin tiêu đề, tôi biết chính xác nơi để tìm kiếm/nơi thực hiện của nó. Ngược lại, nếu tôi cần hiển thị giao diện của thứ gì đó trước đó bị ẩn trong tệp .cpp (ví dụ: hàm tĩnh trở thành toàn cầu), tôi biết chính xác vị trí đặt nó.

Tôi đã nhìn thấy quá nhiều hậu quả xấu của không phải theo quy tắc đơn giản này. Chức năng nội tuyến không cần thiết, phá vỡ bất kỳ loại quy tắc nào về đóng gói, (tách) giao diện và triển khai, mã không đúng chỗ, đặt tên một vài - tất cả do thực tế là tiêu đề anh chị em thích hợp hoặc tệp cpp chưa bao giờ được thêm vào.

Vì vậy: luôn xác định cả tệp .h và .c. Đặt tiêu chuẩn, theo dõi nó và an toàn dựa trên đó. Cuộc sống đơn giản hơn nhiều theo cách này và đơn giản là điều quan trọng nhất trong phần mềm.

+0

Điều này thật ngu ngốc. Một số thứ không cần triển khai (ví dụ: tiêu đề cấu hình, v.v.). Tiêu chuẩn mã là tốt, có, nhưng cứng nhắc theo họ ngay cả khi họ rõ ràng làm cho bạn làm một cái gì đó ngu ngốc (chẳng hạn như trống '.c' tập tin) không phải là. Ví dụ, trong thư viện của tôi tại thời điểm này tôi có 52 tiêu đề và chỉ có 47 tệp triển khai. – Thomas

2

Bjarne Stroustrup Giải thích nó đẹp trong cuốn sách của ông "The C++ Programming Language" ....

Phong cách tiêu đề duy nhất của phân vùng vật lý là hữu ích nhất khi chương trình là nhỏ và các bộ phận của nó không dành cho sử dụng riêng biệt . Khi không gian tên được sử dụng, cấu trúc logic của chương trình vẫn có thể được giải thích trong một tệp tiêu đề duy nhất.

Đối với các Chương trình lớn hơn, cách tiếp cận tệp tiêu đề duy nhất không hoạt động được trong môi trường phát triển dựa trên tệp thông thường. Một sự thay đổi để các lực lượng tiêu đề phổ biến biên dịch lại toàn bộ chương trình, và các bản cập nhật của tiêu đề đơn đó bởi một số lập trình viên là dễ bị lỗi. Trừ khi nhấn mạnh mạnh mẽ được đặt trên các phong cách lập trình dựa nhiều vào các không gian tên và các lớp, cấu trúc logic sẽ suy giảm khi chương trình phát triển.

Một tổ chức vật lý thay thế cho phép mỗi mô-đun hợp lý có tiêu đề riêng xác định các cơ sở mà nó cung cấp. Mỗi tệp .c sau đó có h tương ứng. tệp chỉ định những gì nó cung cấp (giao diện của nó). Mỗi .c mô-đun bao gồm tệp .h của riêng nó và thường cũng là các tệp .h khác chỉ định những gì cần thiết từ các mô-đun khác để triển khai các dịch vụ được quảng cáo trong giao diện của nó. Tổ chức vật lý này tương ứng với tổ chức hợp lý của một mô-đun. Cách tiếp cận nhiều tiêu đề giúp dễ dàng xác định các phụ thuộc. Cách tiếp cận tiêu đề duy nhất buộc chúng ta xem xét mọi khai báo được sử dụng bởi bất kỳ mô-đun nào và quyết định xem nó có liên quan hay không. Thực tế đơn giản là việc duy trì một mã luôn được thực hiện với thông tin không đầy đủ và từ một phối cảnh cục bộ. Nội địa hóa tốt hơn dẫn đến ít thông tin hơn để biên dịch mô-đun và do đó biên dịch nhanh hơn ..

Các vấn đề liên quan