Thành thật mà nói, tôi nghĩ rằng những lý do duy nhất để đưa ra quyết định như vậy là cú pháp thuận tiện và truyền thống. Tôi sẽ giải thích lý do tại sao bằng cách hiển thị (không) sự khác biệt giữa hai và sự khác biệt này quan trọng như thế nào khi đưa ra quyết định.
Có sự khác biệt nào giữa các chức năng bạn bè không phải thành viên và các chức năng của thành viên công cộng? Không nhiều. Sau khi tất cả, một hàm thành viên chỉ là một hàm bình thường với một tham số this
ẩn và truy cập vào các thành viên riêng của lớp.
// what is the difference between the two inv functions?
// --- our code ---
struct matrix1x1 { // this one is simple :P
private:
double x;
public:
//... blah blah
void inv() { x = 1/x; }
friend void inv(matrix1x1& self) { self.x = 1/self.x; }
};
matrix1x1 a;
// --- client code ---
// pretty much just this:
a.inv();
// vs this:
inv(a);
void lets_try_to_break_encapsulation(matrix1x1& thingy) {
thingy.x = 42; // oops, error. Nope, still encapsulated.
}
Cả hai đều cung cấp chức năng giống nhau và không thay đổi những chức năng khác có thể làm. Cùng một bên nhận được tiếp xúc với thế giới bên ngoài: không có sự khác biệt về mặt đóng gói.Hoàn toàn không có gì mà các chức năng khác có thể làm khác nhau bởi vì có một hàm bạn bè sửa đổi trạng thái riêng tư. Trong thực tế, người ta có thể viết hầu hết các lớp với hầu hết các chức năng như chức năng người bạn không phải thành viên (chức năng ảo và một số toán tử quá tải phải là thành viên) cung cấp cùng một số lượng đóng gói: người dùng không thể viết bất kỳ chức năng người bạn nào khác mà không sửa đổi và không có chức năng nào ngoài chức năng của bạn bè có thể truy cập các thành viên riêng tư. Tại sao chúng ta không làm điều đó? Bởi vì nó sẽ chống lại phong cách của 99,99% lập trình viên C++ và không có lợi thế lớn để được lấy từ nó.
Sự khác biệt nằm trong bản chất của các chức năng và cách bạn gọi chúng. Là một hàm thành viên có nghĩa là bạn có thể lấy một con trỏ đến hàm thành viên từ nó, và là một hàm không phải là thành viên có nghĩa là bạn có thể nhận được một con trỏ hàm tới nó. Nhưng đó là hiếm khi có liên quan (đặc biệt là với hàm bao hàm chung chung như std::function
xung quanh).
Sự khác biệt còn lại là cú pháp. Các nhà thiết kế của ngôn ngữ D quyết định chỉ thống nhất toàn bộ điều và nói rằng bạn có thể gọi một hàm thành viên trực tiếp bằng cách truyền cho nó một đối tượng như inv(a)
và gọi hàm miễn phí làm thành viên của đối số đầu tiên, như a.inv()
. Và không có lớp học đột nhiên bị đóng gói nặng vì điều đó hay bất cứ điều gì.
Để giải quyết những ví dụ cụ thể trong câu hỏi, nên inv
là một thành viên hoặc một tổ chức phi thành viên? Tôi có thể làm cho nó trở thành một thành viên, đối với các đối số thú vị mà tôi đã nêu ở trên. Không theo phong cách, nó không tạo nên sự khác biệt.
. Điều này dường như không xảy ra trong C++ bởi vì vào thời điểm này nó sẽ là một thay đổi phá vỡ, không có lợi ích đáng kể. Nó sẽ, cho một ví dụ cực đoan, phá vỡ các lớp học matrix1x1
tôi đã viết ở trên bởi vì nó làm cho cả hai cuộc gọi mơ hồ.
Có thể có sự đóng gói rõ ràng hơn về chức năng bên trong đối tượng thay vì lan truyền trong các hàm bạn bè khác nhau. – PherricOxide
Liên quan http://stackoverflow.com/a/7821482/46642 –
Tôi nghĩ rằng câu trả lời xứng đáng với upvotes ở đây nên chỉ ra sự khác biệt thực sự giữa các hàm thành viên và bạn bè không phải thành viên và giải thích cách những khác biệt đó sẽ góp phần đưa ra quyết định chứ không phải vẫy tất cả mọi thứ bằng "vì nó cung cấp đóng gói nhiều hơn/ít hơn". –