Các tập tin tiêu đề thực sự tốt? [đóng cửa]


20

Tôi thấy các tệp tiêu đề sẽ hữu ích khi duyệt các tệp nguồn C ++, vì chúng đưa ra một "bản tóm tắt" về tất cả các chức năng và thành viên dữ liệu trong một lớp. Tại sao nhiều ngôn ngữ khác (như Ruby, Python, Java, v.v.) không có tính năng như thế này? Đây có phải là một lĩnh vực mà tính dài dòng của C ++ có ích không?


Tôi không quen thuộc với các công cụ trong C và C ++, nhưng hầu hết các trình soạn thảo IDE / mã mà tôi đã làm việc có chế độ xem "cấu trúc" hoặc "phác thảo".
Michael K

1
Các tập tin tiêu đề không tốt - chúng là cần thiết! - Xem thêm câu trả lời này trên SO: stackoverflow.com/a/1319570/158483
Pete

Câu trả lời:


31

Mục đích ban đầu của các tệp tiêu đề là cho phép biên dịch một lần và mô đun hóa trong C. Bằng cách khai báo các phương thức trước khi chúng được sử dụng, nó chỉ cho phép vượt qua biên dịch đơn. Thời đại này đã qua lâu nhờ các máy tính mạnh mẽ của chúng tôi có thể thực hiện việc biên dịch nhiều lượt mà không gặp vấn đề gì, và đôi khi còn nhanh hơn cả trình biên dịch C ++.

C ++ tương thích ngược với C cần thiết để giữ các tệp tiêu đề, nhưng đã thêm rất nhiều trên đầu trang, dẫn đến thiết kế khá có vấn đề. Thêm trong FQA .

Đối với tính mô đun, các tệp tiêu đề là cần thiết như siêu dữ liệu về mã trong các mô-đun. Ví dụ. phương thức nào (và trong các lớp C ++) có sẵn trong thư viện nào. Rõ ràng là có nhà phát triển viết điều này, bởi vì thời gian biên dịch là tốn kém. Ngày nay, không có vấn đề gì khi trình biên dịch tạo siêu dữ liệu này từ chính mã. Ngôn ngữ Java và .NET làm điều này bình thường.

Vì vậy, không. Tập tin tiêu đề không tốt. Đó là khi chúng tôi vẫn phải có trình biên dịch và trình liên kết trên các đĩa mềm riêng biệt và quá trình biên dịch mất 30 phút. Ngày nay, chúng chỉ cản trở và là dấu hiệu của thiết kế tồi.


8
Tôi đồng ý với hầu hết những gì bạn viết, nhưng đoạn cuối làm cho mọi thứ trở nên đơn giản. Các tệp tiêu đề vẫn phục vụ mục đích hữu ích để xác định giao diện công cộng.
Benjamin Bannier

3
@honk Đó là những gì tôi đã nhận được với câu hỏi của tôi. Nhưng tôi nghĩ rằng các IDE hiện đại (như được đề xuất bởi ElYusubov) loại bỏ điều này.
Matt Fichman

5
Bạn có thể thêm rằng ủy ban C ++ đang làm việc trên các Mô-đun sẽ làm cho C và C ++ có thể hoạt động mà không cần bất kỳ tiêu đề HOẶC với các tiêu đề được sử dụng theo cách hiệu quả hơn. Điều đó sẽ làm cho câu trả lời này công bằng hơn.
Klaim

2
@MattFichman: Tạo một số tệp tiêu đề là một điều (mà hầu hết các biên tập viên / IDE của lập trình viên có thể tự động thực hiện theo một cách nào đó), nhưng điểm quan trọng về API công cộng là nó cần được xác định và thiết kế bởi một con người thực sự.
Benjamin Bannier

2
@honk Vâng. Nhưng không có lý do cho điều này được xác định trong các tệp riêng biệt. Nó có thể cùng mã với việc thực hiện. Giống như C # đang làm điều đó.
Euphoric

17

Mặc dù chúng có thể hữu ích cho bạn như một dạng tài liệu, hệ thống xung quanh các tệp tiêu đề cực kỳ kém hiệu quả.

C được thiết kế sao cho mỗi lần biên dịch xây dựng một mô-đun duy nhất; mỗi tệp nguồn được biên dịch trong một lần chạy riêng của trình biên dịch. Mặt khác, các tệp tiêu đề được đưa vào bước biên dịch đó cho mỗi tệp nguồn tham chiếu chúng.

Điều này có nghĩa là nếu tệp tiêu đề của bạn được bao gồm trong 300 tệp nguồn, thì nó sẽ được phân tích cú pháp và biên dịch lặp đi lặp lại, 300 lần riêng biệt trong khi chương trình của bạn được xây dựng. Điều tương tự chính xác với cùng một kết quả, lặp đi lặp lại. Đây là một sự lãng phí rất lớn thời gian và là một trong những lý do chính khiến các chương trình C và C ++ mất quá nhiều thời gian để xây dựng.

Tất cả các ngôn ngữ hiện đại cố tình tránh sự kém hiệu quả vô lý này. Thay vào đó, thông thường trong các ngôn ngữ được biên dịch, siêu dữ liệu cần thiết được lưu trữ trong đầu ra bản dựng, cho phép tệp được biên dịch hoạt động như một loại tham chiếu tra cứu nhanh mô tả những gì tệp được biên dịch chứa. Tất cả các lợi ích của một tệp tiêu đề, được tạo tự động mà không có công việc bổ sung nào từ phía bạn.

Thay phiên trong các ngôn ngữ được giải thích, mọi mô-đun được tải sẽ ở lại trong bộ nhớ. Tham chiếu hoặc bao gồm hoặc yêu cầu một số thư viện sẽ đọc và biên dịch mã nguồn liên quan, lưu trú cho đến khi chương trình kết thúc. Nếu bạn cũng yêu cầu nó ở nơi khác, không có công việc bổ sung nào vì nó đã được tải.

Trong cả hai trường hợp, bạn có thể "duyệt" dữ liệu được tạo bởi bước này bằng các công cụ của ngôn ngữ. Thông thường, IDE sẽ có một trình duyệt lớp. Và nếu ngôn ngữ có REPL, nó cũng có thể được sử dụng để tạo một bản tóm tắt tài liệu về bất kỳ đối tượng được tải nào.


5
không hoàn toàn đúng - hầu hết, nếu không phải tất cả, các trình biên dịch biên dịch trước các tiêu đề khi chúng nhìn thấy chúng, kể cả chúng trong đơn vị biên dịch tiếp theo cũng không tệ hơn việc tìm khối mã được biên dịch trước. Nó không khác gì, giả sử, mã .NET liên tục 'xây dựng' system.data.types cho mỗi tệp C # bạn biên dịch. Lý do các chương trình C / C ++ mất nhiều thời gian là vì chúng thực sự được biên dịch (và tối ưu hóa). Tất cả các chương trình .NET cần được phân tích cú pháp vào IL, thời gian chạy thực hiện việc biên dịch thực tế thông qua JIT. Điều đó nói rằng, 300 tệp nguồn trong mã C # cũng mất khá nhiều thời gian để biên dịch.
gbjbaanb

7

Không rõ, ý của bạn là gì khi duyệt tệp cho các chức năng và thành viên dữ liệu. Tuy nhiên, hầu hết các IDE đều cung cấp Công cụ để duyệt lớp và xem các thành viên dữ liệu của lớp.

Ví dụ: Visual Studio có Class ViewObject browsercung cấp chức năng yêu cầu của bạn. Giống như trong các ảnh chụp màn hình sau đây.

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây


4

Một nhược điểm nữa của các tệp tiêu đề là chương trình phụ thuộc vào thứ tự bao gồm của chúng và lập trình viên phải thực hiện các bước bổ sung để đảm bảo tính chính xác.

Điều đó, cùng với hệ thống vĩ mô của C (được kế thừa bởi C ++), dẫn đến nhiều cạm bẫy và tình huống khó hiểu.

Để minh họa, nếu một tiêu đề xác định một số biểu tượng sử dụng macro để sử dụng và một tiêu đề khác sử dụng biểu tượng theo kiểu khác như tên cho một chức năng, thì thứ tự bao gồm sẽ ảnh hưởng lớn đến kết quả cuối cùng. Có rất nhiều ví dụ như vậy.


2

Tôi luôn thích các tệp tiêu đề vì chúng cung cấp một dạng giao diện để triển khai, cùng với một số thông tin bổ sung như các biến lớp, tất cả trong một tệp dễ xem.

Tôi thấy rất nhiều mã C # (không cần 2 tệp cho mỗi lớp) được viết bằng 2 tệp cho mỗi lớp - một tệp thực hiện lớp thực tế và một tệp khác có giao diện trong đó. Thiết kế này tốt cho việc chế nhạo (cần thiết trong một số hệ thống) và giúp xác định tài liệu của lớp mà không phải sử dụng IDE để xem siêu dữ liệu đã biên dịch. Tôi đã đi rất xa để nói rằng nó thực hành tốt.

Vì vậy, C / C ++ bắt buộc một giao diện tương đương (sắp xếp) trong các tệp tiêu đề là một điều tốt.

Tôi biết có những người đề xuất các hệ thống khác không thích chúng vì những lý do bao gồm 'việc hack mã sẽ khó khăn hơn nếu bạn phải đặt mọi thứ vào 2 tệp', nhưng thái độ của tôi là việc hack mã không phải là cách thực hành tốt và một khi bạn bắt đầu viết / thiết kế mã với một chút suy nghĩ, thì bạn sẽ xác định các tiêu đề / giao diện là chuyện đương nhiên.


Giao diện và thực hiện trong các tập tin khác nhau? Nếu bạn cần điều này theo thiết kế thì điều này vẫn rất phổ biến để xác định giao diện (hoặc lớp trừu tượng) bằng các ngôn ngữ như Java / C # trong một tệp và ẩn (các) triển khai trong các tệp khác. Điều này không yêu cầu các tập tin tiêu đề cũ và khó khăn.
Petter Nordlander

hả không phải như tôi đã nói - bạn xác định giao diện và cách triển khai lớp trong 2 tệp.
gbjbaanb

2
tốt, điều đó không yêu cầu các tệp tiêu đề và điều đó không làm cho các tệp tiêu đề tốt hơn. Đúng, chúng là cần thiết trong các ngôn ngữ có di sản, nhưng như một khái niệm, chúng không cần thiết để phân chia giao diện / thực hiện trong các tệp khác nhau (thực sự, chúng làm cho nó tồi tệ hơn).
Petter Nordlander

@Petter Tôi nghĩ bạn hiểu nhầm, về mặt khái niệm chúng không khác nhau - một tiêu đề tồn tại để cung cấp giao diện bằng ngôn ngữ C, và mặc dù bạn đúng - bạn có thể kết hợp cả 2 (như nhiều người làm với mã C chỉ có tiêu đề) không phải là một thực hành tốt nhất mà tôi đã thấy theo sau ở nhiều nơi. Ví dụ, tất cả các nhà phát triển C # tôi đã tách giao diện ra khỏi lớp. Đó là một thực hành tốt để làm điều này vì sau đó bạn có thể bao gồm tệp giao diện trong nhiều tệp khác mà không gây ô nhiễm. Bây giờ, nếu bạn nghĩ rằng sự phân chia này là một cách thực hành tốt nhất (đó là) thì cách để đạt được nó trong C là sử dụng các tệp tiêu đề.
gbjbaanb

Nếu các tiêu đề thực sự hữu ích để phân tách giao diện và triển khai, thì những thứ như PImpl sẽ không cần thiết để ẩn các thành phần riêng tư và tách người dùng cuối khỏi phải biên dịch lại chúng. Thay vào đó, chúng ta nhận được điều tồi tệ nhất của cả hai thế giới. Các tiêu đề giả định một mặt tiền nhanh chóng bị loại bỏ, trong khi mở ra vô số cạm bẫy, đòi hỏi mã nồi hơi dài dòng nếu bạn cần phải làm việc xung quanh chúng.
gạch dưới

1

Tôi thực sự sẽ nói rằng các tập tin tiêu đề không tuyệt vời vì chúng làm rối giao diện và thực hiện. Mục đích của lập trình nói chung và đặc biệt là OOP là có giao diện được xác định và ẩn chi tiết triển khai, nhưng tệp tiêu đề C ++ cho bạn thấy cả phương thức, kế thừa và các thành viên công cộng (giao diện) cũng như các phương thức riêng tư và các thành viên riêng (một phần của việc thực hiện). Chưa kể rằng trong một số trường hợp, bạn kết thúc nội tuyến mã hoặc hàm tạo trong tệp tiêu đề và một số thư viện bao gồm mã mẫu trong các tiêu đề, thực sự trộn lẫn việc thực hiện với giao diện.

Mục đích ban đầu, tôi tin, là làm cho mã có thể sử dụng các thư viện, đối tượng khác, v.v. mà không phải nhập toàn bộ nội dung của tập lệnh. Tất cả bạn cần là tiêu đề để biên dịch và liên kết. Nó tiết kiệm thời gian và chu kỳ theo cách đó. Trong trường hợp đó, đó là một ý tưởng hay, nhưng đó chỉ là một cách giải quyết những vấn đề này.

Đối với việc duyệt cấu trúc của chương trình, hầu hết các IDE đều cung cấp khả năng đó và có rất nhiều công cụ sẽ khởi động các giao diện, phân tích mã, dịch ngược, v.v. để bạn có thể thấy những gì đang diễn ra dưới vỏ bọc.

Về lý do tại sao các ngôn ngữ khác không thực hiện tính năng tương tự? Chà, bởi vì các ngôn ngữ khác đến từ những người khác, và những nhà thiết kế / người sáng tạo đó có một tầm nhìn khác về cách mọi thứ nên hoạt động.

Câu trả lời tốt nhất là gắn bó với những gì công việc bạn cần làm, và làm cho bạn hạnh phúc.


0

Trong nhiều ngôn ngữ lập trình, khi một chương trình được chia thành nhiều đơn vị biên dịch, mã trong một đơn vị sẽ chỉ có thể sử dụng những thứ được định nghĩa trong một đơn vị khác nếu trình biên dịch có một số phương tiện để biết những thứ đó là gì. Thay vì yêu cầu trình biên dịch xử lý một đơn vị biên dịch kiểm tra toàn bộ văn bản của mọi đơn vị biên dịch xác định bất kỳ thứ gì được sử dụng trong đơn vị hiện tại, tốt nhất là để trình biên dịch nhận, trong khi xử lý từng đơn vị, toàn bộ đơn vị sẽ được biên dịch cùng với một ít thông tin từ tất cả những người khác.

Trong C, lập trình viên chịu trách nhiệm tạo hai tệp cho mỗi đơn vị - một tệp chứa thông tin sẽ chỉ cần khi biên dịch đơn vị đó và một tệp chứa thông tin cũng sẽ cần khi biên dịch các đơn vị khác. Đây là một thiết kế khá khó chịu, hacky, nhưng nó tránh được việc phải có một tiêu chuẩn ngôn ngữ chỉ định bất cứ điều gì về cách trình biên dịch sẽ tạo và xử lý bất kỳ tệp trung gian nào. Nhiều ngôn ngữ khác sử dụng một cách tiếp cận khác nhau, trong đó trình biên dịch sẽ tạo từ mỗi tệp nguồn một tệp trung gian mô tả mọi thứ trong tệp nguồn đó được cho là có thể truy cập được bằng mã bên ngoài. Cách tiếp cận này tránh sự cần thiết phải có thông tin trùng lặp trong hai tệp nguồn, nhưng nó yêu cầu nền tảng biên dịch phải xác định ngữ nghĩa tệp theo cách không bắt buộc đối với C.

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.