Mặc dù bạn có thể bao gồm .cpp
các tệp như bạn đã đề cập, đây là một ý tưởng tồi.
Như bạn đã đề cập, khai báo thuộc về tệp tiêu đề. Chúng không gây ra vấn đề gì khi được bao gồm trong nhiều đơn vị biên dịch vì chúng không bao gồm việc triển khai. Bao gồm định nghĩa của một hàm hoặc thành viên lớp nhiều lần thường sẽ gây ra sự cố (nhưng không phải luôn luôn) vì trình liên kết sẽ bị lẫn lộn và gây ra lỗi.
Điều nên xảy ra là mỗi .cpp
tệp bao gồm các định nghĩa cho một tập hợp con của chương trình, chẳng hạn như một lớp, nhóm các hàm được tổ chức hợp lý, các biến tĩnh toàn cục (sử dụng một cách tiết kiệm nếu có), v.v.
Mỗi đơn vị biên dịch ( .cpp
tệp) sau đó bao gồm bất kỳ khai báo nào cần để biên dịch các định nghĩa mà nó chứa. Nó theo dõi các chức năng và các lớp mà nó tham chiếu nhưng không chứa, vì vậy trình liên kết có thể giải quyết chúng sau này khi nó kết hợp mã đối tượng vào một thư viện hoặc thư viện thực thi.
Thí dụ
Foo.h
-> chứa khai báo (giao diện) cho lớp Foo.
Foo.cpp
-> chứa định nghĩa (triển khai) cho lớp Foo.
Main.cpp
-> chứa phương thức chính, điểm vào chương trình. Mã này khởi tạo một Foo và sử dụng nó.
Cả hai Foo.cpp
và Main.cpp
cần bao gồm Foo.h
. Foo.cpp
cần nó bởi vì nó đang xác định mã hỗ trợ giao diện lớp, vì vậy nó cần biết giao diện đó là gì. Main.cpp
cần nó bởi vì nó đang tạo ra một Foo và gọi hành vi của nó, vì vậy nó phải biết hành vi đó là gì, kích thước của Foo trong bộ nhớ và cách tìm các chức năng của nó, v.v. nhưng nó chưa cần triển khai thực tế.
Trình biên dịch sẽ tạo Foo.o
từ Foo.cpp
đó chứa tất cả mã lớp Foo ở dạng biên dịch. Nó cũng tạo ra Main.o
bao gồm phương thức chính và các tham chiếu chưa được giải quyết đến lớp Foo.
Bây giờ đến trình liên kết, kết hợp hai tệp đối tượng Foo.o
và Main.o
thành một tệp thực thi. Nó thấy các tham chiếu Foo chưa được giải quyết trong Main.o
nhưng thấy Foo.o
có chứa các ký hiệu cần thiết, do đó, nó "kết nối các dấu chấm" để nói. Một lệnh gọi hàm Main.o
hiện được kết nối với vị trí thực tế của mã được biên dịch để khi chạy, chương trình có thể nhảy đến vị trí chính xác.
Nếu bạn đã bao gồm Foo.cpp
tệp trong Main.cpp
đó, sẽ có hai định nghĩa về lớp Foo. Trình liên kết sẽ thấy điều này và nói "Tôi không biết nên chọn cái nào, vì vậy đây là một lỗi." Bước biên dịch sẽ thành công, nhưng liên kết thì không. (Trừ khi bạn không biên dịch Foo.cpp
nhưng tại sao nó lại nằm trong một .cpp
tệp riêng ?)
Cuối cùng, ý tưởng về các loại tệp khác nhau không liên quan đến trình biên dịch C / C ++. Nó biên dịch "tệp văn bản" hy vọng chứa mã hợp lệ cho ngôn ngữ mong muốn. Đôi khi nó có thể nói ngôn ngữ dựa trên phần mở rộng tập tin. Ví dụ, biên dịch một .c
tệp không có tùy chọn trình biên dịch và nó sẽ giả sử C, trong khi một .cc
hoặc .cpp
phần mở rộng sẽ bảo nó giả sử C ++. Tuy nhiên, tôi có thể dễ dàng yêu cầu một trình biên dịch biên dịch một tệp .h
hoặc thậm chí .docx
là C ++ và nó sẽ phát ra một .o
tệp object ( ) nếu nó chứa mã C ++ hợp lệ ở định dạng văn bản thuần túy. Các phần mở rộng này là nhiều hơn cho lợi ích của lập trình viên. Nếu tôi thấy Foo.h
và Foo.cpp
, tôi ngay lập tức giả định rằng cái đầu tiên chứa khai báo của lớp và cái thứ hai chứa định nghĩa.