2009-05-17 24 views
14

Tôi chỉ tự hỏi, toàn bộ các điểm tách các lớp thành một tệp .h và a .cpp là gì? Nó làm cho nó khó khăn hơn để chỉnh sửa, và nếu lớp học của bạn sẽ không được biên dịch thành một .lib hoặc .dll để sử dụng bên ngoài, điểm là gì?Tại sao đặt một khai báo lớp và định nghĩa trong hai tập tin riêng biệt trong C + +?

Chỉnh sửa: Lý do tôi yêu cầu là thư viện Boost đặt mọi thứ vào tệp .hpp, (hầu hết các thư viện) và tôi muốn biết lý do tại sao nó được phân tách trong hầu hết các mã khác Tôi hiểu rồi.

Trả lời

15

C++ có một thứ gọi là Quy tắc một định nghĩa. Nó có nghĩa là (không bao gồm các hàm nội tuyến), các định nghĩa chỉ có thể xuất hiện trong một đơn vị biên dịch. Vì các tệp tiêu đề C++ chỉ là "sao chép và dán" ở mọi tệp bao gồm, bây giờ bạn đang đặt định nghĩa ở nhiều nơi nếu bạn chỉ cần đặt các định nghĩa trong các tệp tiêu đề.

Tất nhiên, bạn có thể nói, tại sao không làm mọi thứ nội tuyến. Vâng, nếu trình biên dịch tôn trọng đề xuất nội tuyến của bạn, mã cho các chức năng dài sẽ được sao chép tại mọi trang web cuộc gọi, làm cho mã của bạn quá lớn và có thể gây ra sự cố, vấn đề bộ nhớ cache và tất cả các loại công cụ không thích hợp. Mặt khác, nếu trình biên dịch không nghe bạn, và không inline bất cứ điều gì, bây giờ bạn có 2 vấn đề: 1) bạn không biết đơn vị dịch nào có định nghĩa lớp học của bạn, và 2) trình biên dịch vẫn phải lội qua các định nghĩa của bạn mỗi khi bạn # bao gồm chúng. Hơn nữa, không có cách nào dễ dàng để đảm bảo bạn đã không vô tình xác định cùng một phương thức hai lần, trong 2 tiêu đề khác nhau, khác nhau.

Bạn cũng nhận được sự cố phụ thuộc vòng tròn. Đối với một lớp để gọi phương thức của lớp khác, lớp đó cần phải được khai báo trước. Vì vậy, nếu 2 lớp cần gọi các phương thức của nhau, mỗi lớp phải được khai báo trước khi có thể được định nghĩa. Không có cách nào để làm điều này với các khai báo và định nghĩa trong một tập tin.

Thực sự, đó là cách ngôn ngữ và trình phân tích cú pháp được tạo. Đó là một nỗi đau, nhưng bạn chỉ phải đối phó với nó.

0

Vì trong hầu hết các trường hợp, bạn sẽ muốn sử dụng lớp ở đâu đó bên cạnh tệp mà bạn triển khai nó. Nếu bạn thực hiện toàn bộ chương trình trong một tệp, bạn không cần sự tách biệt.

Bạn hầu như không bao giờ muốn viết chương trình C++ tất cả trong một tệp.

+0

Tôi có nghĩa là đặt định nghĩa toàn bộ lớp vào a. H và bao gồm nó, trong khi bảo vệ nó với tiêu đề bảo vệ –

+0

điều gì xảy ra nếu bạn bao gồm tệp .h ở nhiều nơi trong cùng một tệp thực thi? Trả lời: bạn kết thúc với nhiều định nghĩa của cùng một hàm. - Điểm thực sự ở đây là bạn có thể làm tất cả những thứ ngốc nghếch với các quy tắc, nhưng chúng hầu như luôn luôn không hoạt động tốt trong thực tế như những cách bình thường. –

3

Vâng, một trong những lợi ích của việc có mã theo cách này là nó giảm thời gian biên dịch.

Hãy nói rằng bạn có những tập tin trên dự án của bạn:

  • ah
  • a.cpp
  • b.cpp

Nếu bạn đã có a.cpp biên soạn thành một đối tượng file ao, sau đó nếu bạn bao gồm ah trong b.cpp, compilati sẽ nhanh hơn vì trình phân tích cú pháp sẽ không phải xử lý toàn bộ việc khai báo/định nghĩa của a.

2

Bởi vì ngay cả trong các lớp học DLL của bạn cũng sẽ sử dụng lớp học của bạn. Các tệp đó phải xem khai báo lớp tại thời gian biên dịch, bằng cách bao gồm .h. Họ không được xem định nghĩa hoặc sẽ có nhiều định nghĩa về các hàm lớp.

0

Trong C++, việc biên dịch riêng các mô-đun mã (các tệp .c hoặc .cpp) yêu cầu các nguyên mẫu hàm được xác định trước khi sử dụng. Nếu bạn muốn sử dụng các lớp hoặc các phương thức được định nghĩa ở một nơi khác, bạn phải nhập các tệp .h để nhận định nghĩa của chúng. Cuối cùng, trình liên kết đảm bảo tất cả các lời hứa được thực hiện trong các tệp .h có thể được thực hiện bởi tất cả các tệp c/cpp.

Ngoài ra, nó cho phép tạo toàn bộ khung như chỉ tăng bằng cách xác định tệp .h.

+1

Tăng chủ yếu bao gồm các tệp tiêu đề vì dựa nhiều vào các mẫu C++ (ngụ ý đặt định nghĩa toàn bộ lớp trong tiêu đề của nó khi nó được tạo mẫu). Theo tôi nhớ một số phần của khung công tác này có tệp cpp, một tệp không dựa trên các mẫu C++: ví dụ thư viện luồng. –

2

Chỉnh sửa của bạn lại: Boost tạo sự khác biệt quan trọng. Các lớp mẫu hầu như luôn được định nghĩa trong các tiêu đề vì cách trình biên dịch và trình liên kết hoạt động trong tiêu chuẩn C++ hiện tại. Bạn sẽ tìm thấy hầu hết các thư viện mẫu (không chỉ là Boost) được triển khai trong các tệp tiêu đề cho cùng một lý do.

5

Tăng không trực tiếp tất cả mã của nó; nó định nghĩa các định nghĩa mẫu cho các lớp mà nó mong đợi người tiêu dùng của nó sẽ khởi tạo, như shared_ptr. Nhiều thư viện có các phần cần được biên dịch riêng biệt, như boost :: serialization và program_options.

Nội tuyến có thể có tác động tiêu cực nghiêm trọng khi kích thước cơ sở mã của bạn tăng lên. Nó làm tăng sự ghép nối giữa các thành phần của bạn, chưa kể đến việc nuking thời gian biên dịch của bạn (mà tăng lên vì nhiều lý do khác :). Hiệu quả, tất cả các đơn vị dịch thuật của bạn sẽ có gần như một bản sao hoàn chỉnh của chương trình, và một thay đổi nhỏ sẽ khiến bạn phải xây dựng lại/kiểm tra lại mọi thứ. Trên một số dự án, điều này có thể mất nhiều, nhiều giờ.

Tôi chưa bao giờ thực sự nhận thấy khó chỉnh sửa hơn; theo kinh nghiệm của tôi, nó làm cho nó dễ dàng hơn vì sự tách biệt rõ ràng về giao diện và việc triển khai, và tôi biết nên tìm tệp nào để tìm những gì tôi đang tìm kiếm.

+0

Câu trả lời hay! Tôi chỉ thêm rằng tiếc là việc tách giao diện và triển khai không rõ ràng như người ta muốn nó, vì một phần của việc rò rỉ thực hiện vào tiêu đề dưới dạng các trường dữ liệu riêng (vì nếu không trình biên dịch sẽ không biết kích thước của lớp). Thành ngữ pimpl được sử dụng để giải quyết vấn đề này. –

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