Các cách tổ chức giao diện và triển khai trong C ++


12

Tôi đã thấy rằng có một số mô hình khác nhau trong C ++ liên quan đến những gì đi vào tệp tiêu đề và những gì đối với tệp cpp. AFAIK, hầu hết mọi người, đặc biệt là những người từ nền C, làm:

foo.h

 class foo {
 private:
     int mem;
     int bar();
 public:
     foo();
     foo(const foo&);
     foo& operator=(foo);
     ~foo();
 }

foo.cpp

 #include foo.h
 foo::bar() { return mem; }
 foo::foo() { mem = 42; }
 foo::foo(const foo& f) { mem = f.mem; }
 foo::operator=(foo f) { mem = f.mem; }
 foo::~foo() {}
 int main(int argc, char *argv[]) { foo f; }

Tuy nhiên, các giảng viên của tôi thường dạy C ++ cho những người mới bắt đầu như thế này:

foo.h

 class foo {
 private:
     int mem;
     int bar() { return mem; }
 public:
     foo() { mem = 42; }
     foo(const foo& f) { mem = f.mem; }
     foo& operator=(foo f) { mem = f.mem; }
     ~foo() {}
 }

foo.cpp

 #include foo.h
 int main(int argc, char* argv[]) { foo f; }
 // other global helper functions, DLL exports, and whatnot

Ban đầu đến từ Java, tôi cũng luôn bị mắc kẹt theo cách thứ hai này vì một số lý do, chẳng hạn như tôi chỉ phải thay đổi thứ gì đó ở một nơi nếu tên giao diện hoặc phương thức thay đổi, tôi thích cách thụt lề khác nhau của các lớp trong khi tôi nhìn vào thực hiện của họ, và rằng tôi thấy tên dễ đọc foohơn so với foo::foo.

Tôi muốn thu thập pro và con cho cả hai cách. Có lẽ vẫn còn những cách khác?

Tất nhiên, một nhược điểm của tôi là sự cần thiết phải khai báo chuyển tiếp.


2
foo.cppbây giờ không có gì để làm với foolớp của bạn và nên để trống (có thể nhưng #includeđể làm cho hệ thống xây dựng của bạn hài lòng).
Benjamin Bannier

2
Giảng viên của bạn là điên rồ.
Cuộc đua nhẹ nhàng với Monica

Câu trả lời:


16

Trong khi phiên bản thứ hai dễ viết hơn, đó là giao diện pha trộn với việc thực hiện.

Các tệp nguồn bao gồm các tệp tiêu đề cần được biên dịch lại mỗi khi các tệp tiêu đề được thay đổi. Trong phiên bản đầu tiên, bạn chỉ thay đổi tệp tiêu đề nếu bạn cần thay đổi giao diện. Trong phiên bản thứ hai, bạn sẽ thay đổi tệp tiêu đề nếu bạn cần thay đổi giao diện hoặc cách triển khai.

Bên cạnh đó, bạn không nên tiết lộ chi tiết triển khai , bạn sẽ nhận được biên dịch lại không cần thiết với phiên bản thứ hai.


1
+1 Trình lược tả của tôi không ghi mã được đặt trong tệp tiêu đề - đó cũng là một lý do có giá trị.
Eugene

Nếu bạn thấy câu trả lời của tôi cho câu hỏi này programmers.stackexchange.com/questions/4573/... bạn sẽ thấy làm thế nào mà phụ thuộc rất nhiều vào ngữ nghĩa của lớp, tức là những gì sẽ được sử dụng nó (đặc biệt nếu nó là một phần tiếp xúc của giao diện người dùng của bạn và có bao nhiêu lớp khác trong hệ thống của bạn sử dụng nó trực tiếp).
CashCow

3

Tôi đã làm điều đó theo cách thứ hai trong '93 -95. Mất vài phút để biên dịch lại một ứng dụng nhỏ với 5-10 chức năng / tệp (trên cùng 486 PC đó .. và không, tôi cũng không biết về các lớp học, tôi chỉ mới 14-15 tuổi và không có internet ) .

Vì vậy, những gì bạn dạy cho người mới bắt đầu và những gì bạn sử dụng một cách chuyên nghiệp là rất nhiều kỹ thuật khác nhau, đặc biệt là trong C ++.

Tôi nghĩ rằng so sánh giữa C ++ và một chiếc xe F1 là apt. Bạn không đặt người mới bắt đầu vào một chiếc xe F1 (thậm chí không khởi động trừ khi bạn làm nóng trước động cơ đến 80-95 độ celcius).

Đừng dạy C ++ là ngôn ngữ đầu tiên. Bạn nên có đủ kinh nghiệm để biết lý do tại sao tùy chọn 2 kém hơn so với tùy chọn 1 nói chung, biết một chút về việc biên dịch / liên kết tĩnh có nghĩa là gì và do đó hiểu tại sao C ++ thích nó theo cách đầu tiên.


Câu trả lời này sẽ còn tuyệt vời hơn nữa nếu bạn xây dựng một chút về biên dịch / liên kết tĩnh (lúc đó tôi không biết nó nữa!)
Felix Dombek

2

Phương thức thứ hai là cái mà tôi sẽ gọi là một lớp hoàn toàn nội tuyến. Bạn đang viết một định nghĩa lớp nhưng tất cả mã của bạn sử dụng nó sẽ chỉ nội tuyến mã.

Có, trình biên dịch quyết định khi nào nội tuyến và khi nào không ... Trong trường hợp này, bạn đang giúp trình biên dịch đưa ra quyết định và cuối cùng bạn sẽ thực sự tạo ra ít mã hơn và có khả năng nhanh hơn.

Ưu điểm này có khả năng lớn hơn thực tế là nếu bạn sửa đổi việc thực hiện một chức năng, bạn cần xây dựng lại tất cả các nguồn sử dụng nó. Trong bản chất nhẹ của lớp, bạn sẽ không sửa đổi việc thực hiện. Nếu bạn thêm một phương thức mới, dù sao bạn cũng sẽ phải sửa đổi tiêu đề.

Tuy nhiên, khi lớp học của bạn trở nên phức tạp hơn, thậm chí thêm một vòng lặp, lợi ích của việc thực hiện theo cách này sẽ giảm xuống.

Nó vẫn có những ưu điểm của nó, đặc biệt:

  • Nếu đây là mã "chức năng chung", bạn có thể chỉ cần bao gồm tiêu đề và sử dụng nó từ nhiều dự án mà không phải liên kết với thư viện chứa nguồn của nó.

Nhược điểm của nội tuyến trở thành một vấn đề khi nó có nghĩa là bạn phải đưa các chi tiết triển khai vào tiêu đề của mình, tức là bạn phải bắt đầu bao gồm các tiêu đề bổ sung.

Lưu ý rằng các mẫu là một trường hợp đặc biệt vì bạn phải đưa vào chi tiết triển khai. Bạn có thể che khuất nó trong một tập tin khác nhưng nó cần phải ở đó. (Có một ngoại lệ đối với quy tắc đó với các cảnh báo nhưng nói chung bạn nội tuyến các mẫu của bạn).


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.