Trường hợp sử dụng để sử dụng lớp bạn bè C ++ là gì?


8

Tôi đang cố gắng để hiểu người bạn C ++. Khi nào là trường hợp sử dụng tốt để sử dụng bạn bè? Tôi cho rằng nếu chúng ta muốn để một lớp khác có quyền truy cập vào các thuộc tính của lớp khác, tại sao chúng ta không biến nó thành công khai hoặc kế thừa từ lớp đó?

Cảm ơn sự giúp đỡ của bạn.


4
Tôi nghĩ rằng câu hỏi này chắc chắn thuộc về đây vì nó nhiều hơn một "tại sao?" câu hỏi hơn một "làm thế nào?" câu hỏi, và đã có một số điểm tốt được thực hiện trong các câu trả lời.
Larry Coleman

Câu trả lời:


14

Làm cho một thành viên của lớp publiccó nghĩa là cấp cho mọi người quyền truy cập vào nó, do đó phá vỡ hoàn toàn việc đóng gói.

Kế thừa từ một lớp thường không được mong muốn, nếu lớp bạn không có nghĩa là một lớp con. Phân lớp chỉ để có quyền truy cập vào bên trong của một lớp là một lỗi thiết kế nghiêm trọng. Và thậm chí một lớp con không thể nhìn thấy các thành viên riêng của lớp cơ sở.

Một cách sử dụng thông thường friendlà dành cho các toán tử không thể là thành viên, chẳng hạn như toán tử luồng, operator+v.v. Trong các trường hợp này, lớp thực tế mà chúng được liên kết không phải (luôn luôn) là tham số đầu tiên của hàm, vì vậy hàm không thể được thực hiện như một phương thức thành viên.

Một ví dụ khác là triển khai một trình vòng lặp cho một bộ sưu tập. Trình lặp (và chỉ trình lặp) cần xem các phần bên trong của bộ sưu tập mẹ của nó, tuy nhiên nó không phải là một lớp con của bộ sưu tập.


Đôi khi, các hàm bạn bè miễn phí đó được định nghĩa nội tuyến trong lớp.
Ded

3

Ví dụ tôi thường thấy các lớp bạn bè được sử dụng trong C ++ là trong thử nghiệm đơn vị. Các bài kiểm tra đơn vị thường muốn biết tất cả về nội bộ của bạn, nhưng không phải là một phần của bạn và sẽ không có ý nghĩa gì khi họ cố gắng kế thừa từ bạn.

Nếu bạn không quen với việc viết bài kiểm tra đơn vị, tôi khuyên bạn nên bắt đầu ngay.


6
Các lớp sản xuất không nên biết gì về các lớp kiểm tra đơn vị. Và các bài kiểm tra không cần phải xem nội bộ của lớp được kiểm tra; một lớp được thiết kế tốt có thể được kiểm tra đơn vị thông qua API công khai. Nếu không, rất có thể đó không phải là một lớp được thiết kế tốt (ví dụ: nó cố gắng làm quá nhiều, vì vậy một phần chức năng của nó nên được trích xuất tốt hơn thành một lớp riêng biệt).
Péter Török

4
@ peter-torok: Điều đó phụ thuộc vào ý nghĩa của "thiết kế tốt" đối với bạn. Một trường hợp phổ biến mà tôi muốn truy cập đặc quyền là tiêm giả để theo dõi xem các hành động được cho là đã xảy ra. Việc phơi bày những phần đó của cấu trúc đối tượng trong API công khai có vẻ như là một vi phạm lớn về đóng gói. Vì vậy, tôi thường muốn mã kiểm tra đơn vị có quyền truy cập đặc quyền.
btilly

1
@btilly: Nếu bạn muốn tiêm giả, bạn có thể sử dụng một tham số trong hàm tạo, không cần phải truy cập vào các phần bên trong của lớp.
Giorgio

1
@ PéterTörök GTest không đồng ý với bạn. Có một số trường hợp trong đó một bài kiểm tra bạn bè sẽ hữu ích - ví dụ, người ta có thể muốn so sánh sự tương đương của hai đối tượng của cùng một lớp mà không để lộ hoạt động tương đương trong API (có lẽ vì lo ngại về bảo mật).
VF1

1
@Giorgio Có, bạn có thể đặt thông tin mà bạn muốn kiểm tra đơn vị ở chế độ công khai. Nhưng bây giờ API công khai của bạn đã phát triển và có thể khóa bạn để thiết kế các quyết định sẽ không khôn ngoan trong tương lai. Nói cách khác, nó là thích hợp để có các bài kiểm tra đơn vị cho các trường hợp cạnh của việc thực hiện hiện tại của bạn. Không phải lúc nào cũng thích hợp để đưa ra mức độ chi tiết đó cho người tiêu dùng của lớp bạn.
btilly

0

Câu hỏi này nên có trên stackoverflow. Dù sao, bạn chỉ sử dụng một người bạn khi bạn muốn chia sẻ các phần riêng tư của lớp mình với một lớp khác (hoặc các lớp) chứ không phải với bất kỳ ai khác. Nếu bạn công khai chúng, mọi người đều có thể thấy các phần riêng tư của bạn (ý định chơi chữ ;-P). Có hai hạn chế quan trọng thực thi quyền riêng tư: 1) bạn phải chỉ định ai là bạn của mình. Không ai khác có thể là một người tự do. 2) bạn không thể kế thừa hành vi "thân thiện" trong các lớp con của lớp bạn


1
Câu hỏi về phương pháp Socrates: Và tại sao mọi người lại thấy những phần riêng tư của bạn thật tệ?
Larry Coleman

3
@Larry: tùy thuộc vào việc nó được thực hiện một cách trang nhã hay cho giá trị sốc: D
Matt Ellen

@Matt: vậy có sử dụng hợp lý các biến công khai không?
Larry Coleman

@Larry: (không phải là uyển ngữ) khi thay đổi trạng thái của đối tượng, ngoài tầm kiểm soát của đối tượng, sẽ không làm cho đối tượng ở trạng thái không hợp lệ. Tôi nghĩ rằng các sự kiện trong c # tính trong khía cạnh này.
Matt Ellen

0

Một cách sử dụng thông thường là cấp quyền truy cập vào các chức năng tạo thành một phần của giao diện của lớp, nhưng nhờ các quy tắc khác thực sự không thể là một phần của lớp. Trình chèn / trích xuất cho iostream là một ví dụ cổ điển:

namespace whatever { 
    class something { 
        // ...    
        friend std::ostream &operator<<(std::ostream &os, something const &thing);
        friend std::istream &operator>>(std::istream &is, something &thing);
    };
}

Làm cho các thành viên toán tử của lớp sẽ không hoạt động. Một thao tác I / O trông giống như:

whatever::something thing;

std::cout << thing;

Đối với một toán tử được triển khai như một hàm thành viên, điều này sẽ được giải quyết như sau:

std::cout.operator<<(thing);

Tức là, chức năng sẽ phải là một thành viên std::cout, không phải của something. Vì chúng tôi không muốn sửa đổi std::ostreamliên tục, tùy chọn hợp lý duy nhất của chúng tôi là quá tải toán tử với chức năng miễn phí thay vì chức năng thành viên. Điều đó cho chúng ta hai khả năng: hoặc bỏ qua việc đóng gói hoàn toàn, và làm cho mọi thứ trong lớp trở nên công khai, hoặc nếu không thì giữ kín, nhưng cấp quyền truy cập vào một vài điều thực sự cần nó.

Làm cho một lớp khác một người bạn là khá ít phổ biến. Trong trường hợp này, bạn thường tạo ra một cái gì đó theo thứ tự của một mô-đun hoặc hệ thống con - một tập hợp các lớp hoạt động cùng nhau và có một số mức độ truy cập đặc biệt với nhau mà không được cấp cho thế giới nói chung.


0

Có một ví dụ khá hay về nhu cầu về các lớp bạn bè trong yêu cầu này đối với các lớp giống như bạn bè trong C # , một ngôn ngữ không có chúng.

Trích dẫn bán buôn ở đây:

Công cụ sửa đổi truy cập trong .NET được thiết kế với ý tưởng rằng bạn có thể tin tưởng đồng nghiệp của mình để biết cách mã hoạt động và do đó không bị trượt và đưa ra lỗi.

Ý tưởng này là thiếu sót mặc dù. Phát triển phần mềm phức tạp đến mức một lập trình viên có kinh nghiệm biết thậm chí không tin tưởng anh ấy / cô ấy! Vì lý do này, các lập trình viên có kinh nghiệm thích viết mã, theo thiết kế, không thể bị phá vỡ thông qua việc sử dụng sai. .NET cung cấp các công cụ để thực hiện việc này khi chỉ có một lớp tham gia, nhưng thời điểm lập trình viên cố gắng tạo ra một tập hợp chống đạn gồm hai hoặc ba lớp tương tác, điều này bị phá vỡ.

Cách tiếp cận dành cho .NET là đánh dấu các thành viên được chia sẻ là "nội bộ", cho phép các lớp tương tác với các phần bên trong của nhau. Thật không may, điều này cũng cho phép mọi lớp khác trong hội đồng gây rối với bên trong, có thể là do vô tình hoặc cố ý.

Mads Torgersen, C # Language PM, thậm chí đã trả lời đề xuất này, nói rằng cảm giác lo ngại thực sự là hợp lệ, nhưng không chắc chắn về việc triển khai cụ thể được đề xuất ở đó.

C ++ friendchỉ là một trong những cách để giải quyết vấn đề được mô tả.


0

Khi bạn muốn các lớp học của bạn có mối quan hệ một vợ một chồng.

Ví dụ: khi bạn không muốn các lớp không xác định truy cập vào các trang riêng của họ, nhưng các đối tác của họ cần truy cập.


Các trường hợp sử dụng tương tự như các trường hợp nội bộ trong C # và gói trong Java.
Daniel Varod

0

Tôi đã thấy các lớp bạn bè trong C ++ trở nên hữu ích trong đó trong Java tôi đã sử dụng quyền truy cập gói. Đó là, tôi có một nhóm các lớp có liên quan mật thiết đến mức chúng được viết và duy trì như một đơn vị, trong cùng một tệp nguồn. Bất cứ ai làm việc trên một trong số họ thực sự làm việc trên tất cả chúng. Những bất biến mà họ duy trì họ duy trì cùng nhau. Thật ý nghĩa khi họ nhìn thấy những người bên trong của nhau để họ có thể duy trì sự bất biến chung của họ.

Những gì không có ý nghĩa là truy cập được bảo vệ. Nếu không an toàn khi đưa một phần nội bộ của tôi ra công chúng, thì sẽ không an toàn khi đưa nó ra bất cứ điều gì kỳ quặc xảy ra sau đó trong một dự án hoàn toàn không liên quan và tuyên bố là lớp con của tôi. Hãy để các lớp con sử dụng giao diện chung của tôi, giống như mọi người khác. (Trừ khi họ là bạn bè, nhưng trong trường hợp đó tôi đã viết chúng và thực hiện chúng trong cùng một nguồn, vì vậy chúng hầu như không thuộc một số dự án khác.)


-1

Hãy nói rằng có một chức năng bên ngoài yêu cầu bạn thực hiện các hoạt động trên các thành viên riêng tư hoặc được bảo vệ của hai lớp khác nhau trong chương trình của bạn. bây giờ, để chức năng đó có được quyền truy cập và thực tế sử dụng các thành viên lớp này, bạn phải biến nó thành bạn của cả hai lớp. Hãy nhớ rằng bằng cách biến nó thành bạn của cả hai lớp này, đừng biến nó thành thành viên của các lớp, nó chỉ đạt được sự ưu tiên để đề cập đến các thành viên tư nhân hoặc được bảo vệ của các lớp này. Tôi có thể nói rằng bạn sẽ cần sử dụng chức năng kết bạn khi bạn muốn hoạt động trên các đối tượng của hai lớp khác nhau. Mặc dù việc lạm dụng chức năng bạn bè và lớp có thể làm giảm giá trị của đóng gói.


2
bài này khá khó đọc (tường văn bản). Bạn có phiền chỉnh sửa ing nó thành một hình dạng tốt hơn?
gnat
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.