Thuật ngữ giao diện trên mạng Cv trong C ++


11

Java phân biệt rõ ràng giữa classinterface. (Tôi tin rằng C # cũng có, nhưng tôi không có kinh nghiệm với nó). Tuy nhiên, khi viết C ++, không có sự phân biệt ngôn ngữ bắt buộc giữa lớp và giao diện.

Do đó, tôi luôn xem giao diện là một cách giải quyết cho việc thiếu nhiều kế thừa trong Java. Làm cho một sự khác biệt như vậy cảm thấy độc đoán và vô nghĩa trong C ++.

Tôi luôn có xu hướng đi theo cách tiếp cận "viết mọi thứ theo cách rõ ràng nhất", vì vậy nếu trong C ++, tôi đã có cái được gọi là giao diện trong Java, ví dụ:

class Foo {
public:
  virtual void doStuff() = 0;
  ~Foo() = 0;
};

và sau đó tôi đã quyết định rằng hầu hết những người thực hiện Foomuốn chia sẻ một số chức năng phổ biến mà tôi có thể sẽ viết:

class Foo {
public:
  virtual void doStuff() = 0;
  ~Foo() {}
protected:
  // If it needs this to do its thing:
  int internalHelperThing(int);
  // Or if it doesn't need the this pointer:
  static int someOtherHelper(int);
};

Mà sau đó làm cho nó không phải là một giao diện theo nghĩa Java nữa.

Thay vào đó, C ++ có hai khái niệm quan trọng, liên quan đến cùng một vấn đề thừa kế cơ bản:

  1. virtual vô sinh
  2. Các lớp không có biến thành viên có thể chiếm không gian thừa khi được sử dụng làm cơ sở

    "Các tiểu dự án lớp cơ sở có thể có kích thước bằng không"

    Tài liệu tham khảo

Trong số những người tôi cố gắng tránh # 1 bất cứ khi nào có thể - thật hiếm khi gặp phải một kịch bản trong đó thực sự là thiết kế "sạch nhất". Tuy nhiên, # 2 là một sự khác biệt tinh tế, nhưng quan trọng giữa sự hiểu biết của tôi về thuật ngữ "giao diện" và các tính năng ngôn ngữ C ++. Kết quả của điều này là tôi hiện tại (gần như) không bao giờ gọi mọi thứ là "giao diện" trong C ++ và nói về các lớp cơ sở và kích thước của chúng. Tôi có thể nói rằng trong ngữ cảnh của "giao diện" C ++ là một cách gọi sai.

Tôi đã nhận thấy rằng mặc dù không có nhiều người tạo ra sự khác biệt như vậy.

  1. Tôi có chịu mất bất cứ điều gì bằng cách cho phép (ví dụ protected) các virtualchức năng không tồn tại trong một "giao diện" trong C ++ không? (Cảm giác của tôi hoàn toàn ngược lại - một vị trí tự nhiên hơn cho mã được chia sẻ)
  2. Là thuật ngữ "giao diện" có ý nghĩa trong C ++ - nó chỉ ngụ ý thuần túy virtualhay sẽ công bằng khi gọi các lớp C ++ không có biến thành viên vẫn là giao diện?

C # có cùng một kế thừa nhiều giao diện, một kế thừa thực hiện như Java, nhưng thông qua việc sử dụng các phương thức mở rộng chung , internalHelperThingcó thể được mô phỏng trong hầu hết các trường hợp.
Sjoerd

Lưu ý rằng đó là thực tế xấu để lộ các thành viên ảo.
Klaim

một công chúng ~Foo() {}trong một lớp trừu tượng là một lỗi dưới (gần như) mọi tình huống.
Sói

Câu trả lời:


11

Trong C ++, thuật ngữ "giao diện" không chỉ có một định nghĩa được chấp nhận rộng rãi - vì vậy, bất cứ khi nào bạn sẽ sử dụng nó, bạn nên nói chính xác ý nghĩa của bạn - một lớp cơ sở ảo có hoặc không có triển khai mặc định, tệp tiêu đề, các thành viên công cộng của một lớp tùy ý và như vậy.

Liên quan đến ví dụ của bạn: trong Java (và tương tự trong C #), mã của bạn có thể ngụ ý phân tách mối quan tâm:

interface IFoo {/*  ... */} // here is your interface

class FooBase implements IFoo 
{
     // make default implementations for interface methods
}

class Foo extends FooBase
{
}

Trong C ++, bạn có thể làm điều này nhưng bạn không bị ép buộc. Và nếu bạn muốn gọi một lớp là một giao diện nếu nó không có biến thành viên, nhưng chứa các triển khai mặc định cho một số phương thức, chỉ cần làm như vậy, nhưng hãy đảm bảo mọi người bạn đang nói để biết ý của bạn.


3
Một ý nghĩa khả dĩ khác của "giao diện", đối với một lập trình viên C ++, là các publicphần của .htệp.
David Thornley

Ngôn ngữ nào là ví dụ mã của bạn? Nếu đó là một nỗ lực để trở thành C ++, tôi sắp khóc ...
Qix - MONICA ĐƯỢC PHÂN BIỆT

@Qix: giữ cho dễ, đọc lại bài đăng của tôi thông thoáng.
Doc Brown

Nếu đó là java thì nó vẫn sai và không tôi không thể chỉnh sửa nó; không đủ nhân vật để thay đổi. Java không sử dụng dấu hai chấm trong việc triển khai / mở rộng, đó là lý do tại sao tôi tự hỏi liệu đó có phải là một nỗ lực tại C ++ không ...
Qix - MONICA ĐƯỢC PHÂN BIỆT

@Qix: nếu bạn tìm thấy nhiều vấn đề cú pháp hơn, bạn có thể giữ chúng như một món quà Giáng sinh :-)
Doc Brown

7

Nghe có vẻ giống như bạn có thể rơi vào cái bẫy gây nhầm lẫn về ý nghĩa của giao diện về mặt khái niệm là cả cách triển khai (giao diện - chữ thường 'i') và trừu tượng hóa (Giao diện - chữ hoa 'tôi' ).

Liên quan đến các ví dụ của bạn, bit mã đầu tiên của bạn chỉ là một lớp. Mặc dù lớp của bạn giao diện theo nghĩa là nó cung cấp các phương thức để cho phép truy cập vào hành vi của nó, nhưng đó không phải là Giao diện theo nghĩa của một tuyên bố Giao diện cung cấp một lớp trừu tượng đại diện cho một loại hành vi mà bạn có thể muốn các lớp triển khai thực hiện. Câu trả lời của Doc Brown cho bài viết của bạn cho bạn thấy chính xác những gì tôi đang nói ở đây.

Các giao diện thường được gọi là "công việc" cho các ngôn ngữ không hỗ trợ nhiều tính kế thừa, nhưng tôi cảm thấy đó là một quan niệm sai lầm hơn là một sự thật phũ phàng (và tôi nghi ngờ mình có thể bị kích động vì đã nói điều đó!). Các giao diện thực sự không liên quan gì đến nhiều kế thừa ở chỗ chúng không yêu cầu được thừa kế hoặc là tổ tiên để cung cấp khả năng tương thích chức năng giữa các lớp hoặc trừu tượng hóa thực hiện cho các lớp. Trên thực tế, họ có thể cho phép bạn loại bỏ hoàn toàn quyền thừa kế nếu bạn muốn triển khai mã của mình theo cách đó - không phải là tôi hoàn toàn khuyên bạn nên tôi chỉ nói rằng bạn có thểlàm đi. Vì vậy, thực tế là bất kể vấn đề thừa kế, Giao diện cung cấp một phương tiện có thể xác định loại lớp, thiết lập các quy tắc xác định cách các đối tượng có thể giao tiếp với nhau, do đó chúng cho phép bạn xác định hành vi mà các lớp của bạn sẽ hỗ trợ mà không cần ra lệnh cho phương pháp cụ thể được sử dụng để thực hiện hành vi đó.

Tôi có chịu mất bất cứ điều gì khi cho phép (ví dụ: được bảo vệ) các chức năng không ảo tồn tại trong một "giao diện" trong C ++ không? (Cảm giác của tôi hoàn toàn ngược lại - một vị trí tự nhiên hơn cho mã được chia sẻ)

Giao diện thuần túy có nghĩa là hoàn toàn trừu tượng, bởi vì nó cho phép định nghĩa một hợp đồng tương thích giữa các lớp mà có thể không nhất thiết phải thừa hưởng hành vi của chúng từ một tổ tiên chung. Khi được triển khai, bạn muốn đưa ra lựa chọn về việc có cho phép hành vi triển khai được mở rộng trong một lớp con hay không. Nếu phương thức của bạn không virtual, bạn sẽ mất khả năng mở rộng hành vi đó sau này nếu bạn quyết định bạn cần tạo các lớp con cháu. Bất kể việc triển khai có ảo hay không, Giao diện xác định tính tương thích hành vi trong chính nó, trong khi lớp cung cấp việc thực hiện hành vi đó cho các thể hiện mà lớp đại diện.

Là thuật ngữ "giao diện" có ý nghĩa trong C ++ - nó chỉ ngụ ý thuần ảo hay sẽ công bằng khi gọi các lớp C ++ không có biến thành viên vẫn là giao diện?

Xin lỗi, nhưng đã lâu lắm rồi tôi mới thực sự viết một ứng dụng nghiêm túc trong C ++. Tôi nhớ lại rằng Giao diện là một từ khóa cho mục đích trừu tượng như tôi đã mô tả chúng ở đây. Tôi sẽ không gọi các lớp C ++ theo bất kỳ loại Giao diện nào, thay vào đó tôi sẽ nói rằng lớp có một giao diện theo nghĩa mà tôi đã nêu ở trên. Theo nghĩa đó, thuật ngữ IS có ý nghĩa, nhưng điều đó thực sự phụ thuộc vào bối cảnh.


1
+1 để phân biệt giữa "có" và "là" một giao diện.
Doc Brown

6

Các giao diện Java không phải là một "cách giải quyết", chúng là một quyết định thiết kế có chủ ý nhằm tránh một số vấn đề với nhiều kế thừa như thừa kế kim cương và để khuyến khích các thực tiễn thiết kế giảm thiểu khớp nối.

Giao diện của bạn với các phương thức được bảo vệ là trường hợp sách giáo khoa cần "thích sáng tác hơn kế thừa". Để trích dẫn từ Sutter và Alexandrescu trong phần có tên đó từ Tiêu chuẩn mã hóa C ++ tuyệt vời của họ :

Tránh thuế thừa kế: Quyền thừa kế là mối quan hệ khớp nối chặt chẽ thứ hai trong C ++, chỉ đứng sau tình bạn. Khớp nối chặt chẽ là không mong muốn và nên tránh khi có thể. Do đó, thích sáng tác hơn kế thừa trừ khi bạn biết rằng cái sau thực sự có lợi cho thiết kế của bạn.

Bằng cách bao gồm các chức năng trợ giúp của bạn trong giao diện của bạn, bạn có thể tiết kiệm một chút gõ ngay bây giờ, nhưng bạn đang giới thiệu khớp nối sẽ làm tổn thương bạn trên đường. Gần như luôn luôn tốt hơn về lâu dài để làm cho các chức năng trợ giúp của bạn tách biệt và vượt qua trong một Foo*.

STL là một ví dụ khá hay về điều này. Càng nhiều hàm trợ giúp càng tốt được kéo ra <algorithm>thay vì nằm trong các lớp container. Ví dụ: vì sort()sử dụng API bộ chứa công cộng để thực hiện công việc của nó, bạn biết rằng bạn có thể thực hiện thuật toán sắp xếp của riêng mình mà không phải thay đổi bất kỳ mã STL nào. Thiết kế này cho phép các thư viện như boost, giúp tăng cường STL thay vì thay thế nó, mà không cần STL cần biết bất cứ điều gì về boost.


2

Tôi có chịu mất bất cứ điều gì khi cho phép (ví dụ: được bảo vệ) các chức năng không ảo tồn tại trong một "giao diện" trong C ++ không? (Cảm giác của tôi hoàn toàn ngược lại - một vị trí tự nhiên hơn cho mã được chia sẻ)

Bạn sẽ nghĩ rằng, nhưng việc đặt một phương thức phi ảo được bảo vệ trong một lớp trừu tượng khác sẽ ra lệnh thực thi cho bất kỳ ai viết một lớp con. Làm như vậy đánh bại mục đích của một giao diện theo nghĩa thuần túy của nó, đó là cung cấp một veneer che giấu những gì bên dưới.

Đây là một trong những trường hợp không có câu trả lời phù hợp với một kích thước và bạn phải sử dụng kinh nghiệm và phán đoán của mình để đưa ra quyết định. Nếu bạn có thể tự tin 100% rằng mọi lớp con có thể có của lớp ảo hoàn toàn khác của bạn Foosẽ luôn cần triển khai phương thức được bảo vệ bar(), thì đó Foolà nơi thích hợp cho nó. Khi bạn có một lớp con Bazkhông cần bar(), bạn phải sống với thực tế là Bazsẽ có quyền truy cập vào mã mà nó không nên hoặc thực hiện sắp xếp lại thứ bậc lớp của bạn. Cái trước không phải là thực hành tốt và cái sau có thể mất nhiều thời gian hơn vài phút để sắp xếp mọi thứ đúng cách ngay từ đầu.

Là thuật ngữ "giao diện" có ý nghĩa trong C ++ - nó chỉ ngụ ý thuần ảo hay sẽ công bằng khi gọi các lớp C ++ không có biến thành viên vẫn là giao diện?

Mục 10.4 của tiêu chuẩn C ++ đưa ra đề cập đến việc sử dụng các lớp trừu tượng để triển khai các giao diện nhưng không định nghĩa chúng chính thức. Thuật ngữ này có ý nghĩa trong bối cảnh khoa học máy tính nói chung và bất kỳ ai có thẩm quyền nên hiểu rằng " Foolà một giao diện cho (bất cứ điều gì)" ngụ ý một số hình thức trừu tượng. Những người tiếp xúc với các ngôn ngữ có cấu trúc giao diện được xác định có thể nghĩ ảo thuần túy, nhưng bất kỳ ai cần thực sự làm việc với Foosẽ xem xét định nghĩa của nó trước khi tiếp tục.


2
Sự khác biệt giữa cung cấp một khai báo ảo thuần túy và khai báo cộng với thực hiện là gì? Trong cả hai trường hợp, phải có một triển khai và bazluôn có thể có một triển khai như thế nào return false;hoặc bất cứ điều gì. Nếu phương thức không áp dụng cho tất cả các lớp con, thì nó không thuộc về lớp trừu tượng cơ sở dưới mọi hình thức.
David Thornley

1
Tôi nghĩ rằng câu cuối cùng của bạn nói rằng chúng ta đang ở trên cùng một trang: không có lý do gì bạn không thể có các phương thức được bảo vệ trong các lớp trừu tượng, nhưng chúng không nên cao hơn trong cây thừa kế hơn là hoàn toàn cần thiết. Tôi chỉ nghĩ rằng nếu một lớp phải thực hiện một triển khai, thì nó không ở đúng vị trí trên cây.
Blrfl
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.