Nạp chồng toán tử: hàm thành viên so với hàm không thành viên?


121

Tôi đọc rằng một toán tử được nạp chồng được khai báo là hàm thành viên là không đối xứng vì nó có thể chỉ có một tham số và tham số khác được truyền tự động là thiscon trỏ. Vì vậy, không có tiêu chuẩn nào tồn tại để so sánh chúng. Mặt khác, toán tử nạp chồng được khai báo là a friendđối xứng vì chúng ta chuyển hai đối số cùng kiểu và do đó, chúng có thể được so sánh với nhau.

Câu hỏi của tôi là khi tôi vẫn có thể so sánh giá trị của con trỏ với một tham chiếu, tại sao bạn bè lại được ưu tiên hơn? (sử dụng phiên bản không đối xứng cho kết quả tương tự như đối xứng) Tại sao thuật toán STL chỉ sử dụng phiên bản đối xứng?


11
Câu hỏi của bạn thực sự chỉ về toán tử nhị phân. Không phải tất cả các toán tử quá tải đều bị hạn chế trong một tham số duy nhất. Toán tử () có thể nhận bất kỳ số lượng tham số nào. Mặt khác, toán tử đơn nguyên không thể có bất kỳ tham số nào.
Charles Salvia


4
Đây là một trong nhiều chủ đề trong C ++ Điều kiện đăng ký hành quá tải
Ben Voigt

Câu trả lời:


148

Nếu bạn định nghĩa hàm nạp chồng toán tử của mình là hàm thành viên, thì trình biên dịch sẽ dịch các biểu thức như s1 + s2thành s1.operator+(s2). Điều đó có nghĩa là, hàm thành viên được nạp chồng toán tử được gọi trên toán hạng đầu tiên. Đó là cách các chức năng thành viên hoạt động!

Nhưng điều gì sẽ xảy ra nếu toán hạng đầu tiên không phải là một lớp? Có một vấn đề lớn nếu chúng ta muốn nạp chồng một toán tử mà toán hạng đầu tiên không phải là một loại lớp, nói đúng hơn double. Vì vậy, bạn không thể viết như thế này 10.0 + s2. Tuy nhiên, bạn có thể viết hàm thành viên được nạp chồng toán tử cho các biểu thức như s1 + 10.0.

Để giải quyết vấn đề thứ tự này, chúng tôi định nghĩa hàm nạp chồng toán tử là friendIF nó cần truy cập privatecác thành viên. Chỉ thực hiện friendkhi cần truy cập các thành viên riêng tư. Nếu không, chỉ cần đặt nó thành chức năng non-friend non-member để cải thiện tính đóng gói!

class Sample
{
 public:
    Sample operator + (const Sample& op2); //works with s1 + s2
    Sample operator + (double op2); //works with s1 + 10.0

   //Make it `friend` only when it needs to access private members. 
   //Otherwise simply make it **non-friend non-member** function.
    friend Sample operator + (double op1, const Sample& op2); //works with 10.0 + s2
}

Đọc những điều này:
Một vấn đề nhỏ về thứ tự trong toán hạng
Cách các hàm không phải thành viên cải thiện tính đóng gói


2
"Làm cho nó friendchỉ khi nó cần phải truy cập members..and tin khi bạn không có / đang chán viết accessors, phải không?
badmaash

4
@Abhi: Chọn lựa của bạn: Đóng gói được cải thiện so với thói quen viết lười biếng!
Nawaz

6
@matthias, không phải tất cả các toán tử đều giao hoán. Ví dụ đơn giản là a/b.
edA-qa mort-ora-y

3
Một cách phổ biến để tránh yêu cầu các toán tử không phải friendlà thành viên của bạn là triển khai chúng theo điều kiện của các toán tử gán hoạt động (hầu như chắc chắn sẽ là thành viên công khai). Ví dụ, bạn có thể định nghĩa T T::operator+=(const T &rhs)như một thành viên và sau đó xác định không thành viên T operator(T lhs, const T &rhs)như return lhs += rhs;. Hàm không phải là thành viên nên được định nghĩa trong cùng vùng tên với lớp.
Adrian McCarthy

2
@ricky: Nhưng nếu lhs là một bản sao (như trong nhận xét của tôi), thì việc lhs thay đổi không quan trọng.
Adrian McCarthy

20

Không nhất thiết phải phân biệt giữa friendquá tải toán tử và quá tải toán tử hàm thành viên vì nó là giữa quá tải toán tử toàn cục và quá tải toán tử hàm thành viên.

Một lý do để thích một thế giới quá tải toán tử là nếu bạn muốn cho phép biểu nơi kiểu lớp xuất hiện trên ngay phía bên tay của một nhà điều hành nhị phân. Ví dụ:

Foo f = 100;
int x = 10;
cout << x + f;

Điều này chỉ hoạt động nếu có quá tải toán tử chung cho

Toán tử Foo + (int x, const Foo & f);

Lưu ý rằng toán tử toàn cục quá tải không nhất thiết phải là một friendhàm. Điều này chỉ cần thiết nếu nó cần quyền truy cập vào các thành viên riêng tư của Foo, nhưng không phải lúc nào cũng vậy.

Bất kể, nếu Foochỉ có quá tải toán tử hàm thành viên, như:

class Foo
{
  ...
  Foo operator + (int x);
  ...
};

... thì chúng ta sẽ chỉ có thể có các biểu thức trong đó một Foothể hiện xuất hiện ở bên trái của toán tử cộng.


3
+1 để phân biệt giữa chức năng thành viên và chức năng không phải thành viên hơn là chức năng thành viên và bạn bè. Tôi đoán hôm nay chúng ta sẽ nói "phạm vi toàn cầu hoặc không gian tên".
Adrian McCarthy
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.