Thời gian suy nghĩ - Tại sao bạn vẫn muốn chia nhỏ tệp của mình?
Như tiêu đề cho thấy, vấn đề cuối cùng mà tôi có là nhiều lỗi trình liên kết định nghĩa. Tôi đã thực sự khắc phục vấn đề, nhưng tôi đã không cố định vấn đề một cách chính xác. Trước khi bắt đầu, tôi muốn thảo luận về các lý do để chia nhỏ một tệp lớp thành nhiều tệp. Tôi đã cố gắng để đưa tất cả các kịch bản có thể ở đây - nếu tôi bỏ lỡ bất kỳ, xin vui lòng nhắc nhở tôi và tôi có thể thay đổi. Hy vọng rằng sau đây là đúng:Tách mã lớp C++ thành nhiều tệp, quy tắc là gì?
Lý do 1Để tiết kiệm không gian:
Bạn có một tập tin có chứa các khai báo một lớp học với tất cả các thành viên lớp. Bạn đặt #include guards xung quanh tệp này (hoặC#pragma một lần) để đảm bảo không có xung đột phát sinh nếu bạn #bao gồm tệp trong hai tệp tiêu đề khác nhau sau đó được đưa vào tệp nguồn. Bạn biên dịch một tệp nguồn riêng biệt với việc thực hiện bất kỳ phương thức nào được khai báo trong lớp này, vì nó tải nhiều dòng mã từ tệp nguồn của bạn, làm sạch mọi thứ một chút và giới thiệu một số thứ tự cho chương trình của bạn.
Ví dụ: Như bạn có thể thấy, ví dụ dưới đây có thể được cải thiện bằng cách chia nhỏ việc triển khai các phương thức lớp thành một tệp khác. (Một tập tin cpp)
// my_class.hpp
#pragma once
class my_class
{
public:
void my_function()
{
// LOTS OF CODE
// CONFUSING TO DEBUG
// LOTS OF CODE
// DISORGANIZED AND DISTRACTING
// LOTS OF CODE
// LOOKS HORRIBLE
// LOTS OF CODE
// VERY MESSY
// LOTS OF CODE
}
// MANY OTHER METHODS
// MEANS VERY LARGE FILE WITH LOTS OF LINES OF CODE
}
Lý do 2Để ngăn chặn nhiều lỗi định nghĩa mối liên kết:
Có lẽ đây là lý do chính tại sao bạn sẽ chia thực hiện khai báo. Trong ví dụ trên, bạn có thể di chuyển thân phương thức ra bên ngoài lớp. Điều này sẽ làm cho nó trông sạch hơn và có cấu trúc. Tuy nhiên, theo điều này question, ví dụ trên có ẩn số inline
chỉ định. Di chuyển việc thực hiện từ bên trong lớp ra bên ngoài lớp, như trong ví dụ bên dưới, sẽ gây ra cho bạn các lỗi trình liên kết, và vì vậy bạn sẽ nội tuyến mọi thứ, hoặc di chuyển các định nghĩa hàm tới một tệp .cpp.
Ví dụ: _ Ví dụ bên dưới sẽ gây ra "nhiều lỗi trình liên kết định nghĩa" nếu bạn không di chuyển định nghĩa hàm sang tệp .cpp hoặc chỉ định hàm làm nội dòng.
// my_class.hpp
void my_class::my_function()
{
// ERROR! MULTIPLE DEFINITION OF my_class::my_function
// This error only occurs if you #include the file containing this code
// in two or more separate source (compiled, .cpp) files.
}
Để khắc phục vấn đề này:
//my_class.cpp
void my_class::my_function()
{
// Now in a .cpp file, so no multiple definition error
}
Hoặc:
// my_class.hpp
inline void my_class::my_function()
{
// Specified function as inline, so okay - note: back in header file!
// The very first example has an implicit `inline` specifier
}
Lý do 3Bạn muốn tiết kiệm không gian, một lần nữa, nhưng lần này bạn đang làm việc với một lớp mẫu:
Nếu chúng tôi đang làm việc với các lớp mẫu, thì chúng tôi không thể di chuyển việc triển khai sang tệp nguồn (tệp .cpp). Điều đó hiện không được cho phép bởi (tôi giả định) hoặc là các trình biên dịch tiêu chuẩn hoặc theo hiện tại. Không giống như ví dụ đầu tiên của Lý do 2, ở trên, chúng tôi được phép đặt triển khai trong tệp tiêu đề.Theo điều này question lý do là các phương thức lớp mẫu cũng ngụ ý các chỉ định inline
. Đúng không? (Dường như có ý nghĩa.) Nhưng dường như không ai biết về câu hỏi mà tôi vừa mới tham chiếu!
Vì vậy, hai ví dụ dưới đây giống hệt nhau?
// some_header_file.hpp
#pragma once
// template class declaration goes here
class some_class
{
// Some code
};
// Example 1: NO INLINE SPECIFIER
template<typename T>
void some_class::class_method()
{
// Some code
}
// Example 2: INLINE specifier used
template<typename T>
inline void some_class::class_method()
{
// Some code
}
Nếu bạn có một tập tin mẫu lớp header, mà đang trở thành khổng lồ do cho tất cả các chức năng mà bạn có, sau đó tôi tin rằng bạn được phép di chuyển các định nghĩa chức năng vào một tập tin tiêu đề (thường là một tập tin .tpp?) và sau đó #include file.tpp
ở cuối tệp tiêu đề của bạn có chứa khai báo lớp. Tuy nhiên, bạn KHÔNG được bao gồm tệp này ở bất kỳ nơi nào khác, do đó, .tpp
thay vì .hpp
.
Tôi cho rằng bạn cũng có thể làm điều này với các phương thức nội tuyến của một lớp thông thường? Điều đó cũng được cho phép?
Question Time
Vì vậy, tôi đã thực hiện một số báo cáo trên, hầu hết trong số đó liên quan đến cơ cấu các tập tin nguồn. Tôi nghĩ mọi thứ tôi nói đều đúng, bởi vì tôi đã làm một số nghiên cứu cơ bản và "tìm ra một số thứ", nhưng đây là một câu hỏi và vì vậy tôi không biết chắc chắn.
Điều này tóm tắt, là cách bạn sắp xếp mã trong các tệp. Tôi nghĩ rằng tôi đã tìm ra một cấu trúc sẽ luôn hoạt động.
Đây là những gì tôi đã đưa ra. (Đây là "tiêu chuẩn mã lớp tổ chức tập tin/cấu trúc Ed chim", nếu bạn thích Không biết nếu nó sẽ rất hữu ích nào, đó là điểm của yêu cầu..)
- 1: Khai báo lớp (mẫu hoặc cách khác) trong một tệp
.hpp
, bao gồm tất cả các phương thức, hàm và dữ liệu của bạn bè. - 2: Ở cuối tệp
.hpp
,#include
tệp.tpp
chứa việc triển khai bất kỳ phương thứcinline
nào. Tạo tệp.tpp
và đảm bảo tất cả các phương thức được chỉ định làinline
. - 3: Tất cả các thành viên khác (chức năng phi inline, chức năng người bạn và dữ liệu tĩnh) nên được định nghĩa trong một tập tin
.cpp
, mà#include
s file.hpp
ở phía trên để ngăn chặn các lỗi như "lớp ABC đã không được công bố ". Vì mọi thứ trong tệp này sẽ có liên kết bên ngoài, chương trình sẽ liên kết chính xác.
Các tiêu chuẩn như thế này tồn tại trong ngành? Liệu tiêu chuẩn tôi đưa ra có hoạt động trong mọi trường hợp?
btw Tôi đã bỏ qua câu hỏi của bạn vì câu hỏi quá dài. Giới thiệu về hàm mẫu nội tuyến. Từ khóa nội tuyến không bắt buộc (bạn vẫn được phép đặt chúng trong các tệp tiêu đề) và không có bất kỳ hiệu ứng được bảo đảm nào. Sử dụng từ khóa có thể làm cho trình biên dịch có nhiều khả năng nội tuyến hàm, nhưng không có gì là chắc chắn. Các hàm mẫu này không phải là các hàm nội tuyến ngầm. Câu trả lời cho câu hỏi đó là sai, như đã thảo luận trong các bình luận của câu trả lời. –
@NeilKirk Ah cảm ơn, chắc chắn dường như có rất nhiều bất đồng về nó. – user3728501
Chỉ cần bỏ qua đặt nội tuyến ở đó và vấn đề sẽ không bao giờ làm phiền bạn :) –