Quá tải các toán tử truy cập thành viên -> ,. *


129

Tôi hiểu hầu hết các nhà khai thác quá tải, với ngoại lệ của các nhà khai thác truy cập thành viên ->, .*, ->*, vv

Cụ thể, những gì được truyền cho các hàm toán tử này và những gì cần được trả về?

Làm thế nào để hàm toán tử (ví dụ operator->(...)) biết thành viên nào đang được giới thiệu? Nó có thể biết không? Nó thậm chí cần phải biết?

Cuối cùng, có bất kỳ cân nhắc const cần phải được tính đến? Ví dụ, khi quá tải một cái gì đó như operator[], nói chung, bạn sẽ cần cả phiên bản const và không const. Các nhà khai thác truy cập thành viên yêu cầu các phiên bản const và không const?


1
Tôi tin rằng C ++ ở trên - Faq chạm vào tất cả các câu hỏi của Q ở trên Q.
Alok Lưu

constvà không phải là constphiên bản operator->không bắt buộc , nhưng cung cấp cả hai có thể hữu ích.
Fred Foo


9
@Als: Câu hỏi thường gặp không giải thích cách quá tải ->*.*. Trong thực tế, nó thậm chí không đề cập đến chúng! Tôi cảm thấy họ hiếm khi được tham gia Câu hỏi thường gặp, nhưng tôi sẵn sàng liên kết câu hỏi này từ Câu hỏi thường gặp. Vui lòng không đóng phần này dưới dạng bản sao của Câu hỏi thường gặp!
sbi

@sbi, tôi hoàn toàn thất bại trong việc tìm liên kết đến câu hỏi này từ Câu hỏi thường gặp (tuyệt vời) của bạn và cuối cùng đã hỏi một câu hỏi trùng lặp. Bạn có thể làm cho nó rõ ràng hơn? (xin lỗi nếu nó đã rõ ràng).
P i

Câu trả lời:


144

->

Đây là một trong những thực sự khó khăn duy nhất. Nó phải là một hàm thành viên nonstatic và không cần đối số. Giá trị trả về được sử dụng để thực hiện tra cứu thành viên.

Nếu giá trị trả về là một đối tượng khác của loại lớp, không phải là một con trỏ, thì việc tra cứu thành viên tiếp theo cũng được xử lý bởi một operator->hàm. Điều này được gọi là "hành vi đi sâu." Các ngôn ngữ kết hợp các operator->cuộc gọi cho đến khi cuộc gọi cuối cùng trả về một con trỏ.

struct client
    { int a; };

struct proxy {
    client *target;
    client *operator->() const
        { return target; }
};

struct proxy2 {
    proxy *target;
    proxy &operator->() const
        { return * target; }
};

void f() {
    client x = { 3 };
    proxy y = { & x };
    proxy2 z = { & y };

    std::cout << x.a << y->a << z->a; // print "333"
}

->*

Điều này chỉ khó khăn ở chỗ không có gì đặc biệt về nó. Các phi quá tải phiên bản đòi hỏi một đối tượng của con trỏ đến kiểu lớp ở phía bên tay trái và một đối tượng của con trỏ đến loại thành viên ở bên phải. Nhưng khi bạn quá tải nó, bạn có thể lấy bất kỳ đối số nào bạn thích và trả lại bất cứ điều gì bạn muốn. Nó thậm chí không phải là một thành viên nonstatic.

Nói cách khác, một trong những điều này chỉ là một nhà điều hành nhị phân bình thường như +, -, và /. Xem thêm: Có phải nhà điều hành miễn phí -> * quá tải ác?

.*.

Chúng không thể bị quá tải. Đã có một ý nghĩa tích hợp khi phía bên trái là loại lớp. Có lẽ sẽ có một chút ý nghĩa khi có thể định nghĩa chúng cho một con trỏ ở phía bên trái, nhưng ủy ban thiết kế ngôn ngữ đã quyết định rằng điều đó sẽ gây nhầm lẫn hơn là hữu ích.

Quá tải ->, ->*, ., và .*chỉ có thể điền vào trường hợp một biểu thức sẽ được undefined, nó có thể không bao giờ thay đổi ý nghĩa của một biểu thức đó sẽ là hợp lệ khi không quá tải.


2
Tuyên bố cuối cùng của bạn không hoàn toàn đúng. Ví dụ: bạn có thể quá tải newtoán tử, mặc dù nó hợp lệ ngay cả khi không bị quá tải.
Matt

6
@Matt tốt, newluôn bị quá tải hoặc quy tắc quá tải không thực sự áp dụng cho nó (13,5 / 5: Các hàm phân bổ và phân bổ, toán tử mới, toán tử mới [], xóa toán tử và xóa toán tử [], được mô tả hoàn toàn trong 3.7 .4. các thuộc tính và hạn chế được tìm thấy trong phần còn lại của phân lớp này không áp dụng cho họ trừ khi quy định rõ ràng trong 3.7.4.) Nhưng quá tải unary &hoặc nhị phân &&, ||hoặc ,, hoặc thêm quá tải của operator=, hoặc quá tải bất cứ thứ gì cho một unscoped kiểu liệt kê, có thể thay đổi ý nghĩa của biểu thức. Làm rõ tuyên bố, cảm ơn!
Potatoswatter

41

Toán tử -> là đặc biệt.

"Nó có các ràng buộc bổ sung, không điển hình: Nó phải trả về một đối tượng (hoặc tham chiếu đến một đối tượng) cũng có toán tử quy định con trỏ hoặc nó phải trả về một con trỏ có thể được sử dụng để chọn mũi tên của toán tử điều khiển con trỏ trỏ tới. " Bruce Eckel: Suy nghĩ CPP Vol-one: nhà điều hành->

Các chức năng bổ sung được cung cấp để thuận tiện, vì vậy bạn không phải gọi

a->->func();

Bạn chỉ có thể làm:

a->func();

Điều đó làm cho toán tử -> khác với các toán tử khác quá tải.


3
Câu trả lời này xứng đáng nhận được nhiều tín dụng hơn, bạn có thể tải xuống cuốn sách của Eckel từ liên kết đó và thông tin nằm trong chương 12 của tập một.
P i

26

Bạn không thể quá tải quyền truy cập của thành viên .(tức là phần thứ hai của những gì ->). Tuy nhiên bạn có thể quá tải unary dereferencing điều hành *(tức là phần đầu tiên của những gì ->không).

->Toán tử C ++ về cơ bản là sự kết hợp của hai bước và điều này là rõ ràng nếu bạn nghĩ rằng điều đó x->ytương đương với (*x).y. C ++ cho phép bạn tùy chỉnh những việc cần làm với (*x)phần khi đó xlà một thể hiện của lớp bạn.

Ngữ nghĩa cho ->quá tải là hơi lạ vì C ++ cho phép bạn trả về một con trỏ thông thường (nó sẽ được sử dụng để tìm đối tượng nhọn) hoặc trả về một thể hiện của một lớp khác nếu lớp này cũng cung cấp một ->toán tử. Khi trong trường hợp thứ hai này, việc tìm kiếm đối tượng bị hủy bỏ tiếp tục từ trường hợp mới này.


2
Giải thích tuyệt vời! Tôi đoán điều đó có nghĩa tương tự ->*, vì nó tương đương với hình thức (*x).*?
Chơi lô tô

10

Các ->nhà điều hành không biết những gì thành viên đã được chỉ ra, nó chỉ cung cấp một đối tượng để thực hiện các truy cập thành viên thực tế trên.

Ngoài ra, tôi thấy không có lý do tại sao bạn không thể cung cấp các phiên bản const và không const.


7

Khi bạn quá tải toán tử -> () (không có đối số nào được truyền vào đây), cái mà trình biên dịch thực sự đang gọi -> đệ quy cho đến khi nó trả về một con trỏ thực cho một kiểu. Sau đó, nó sử dụng đúng thành viên / phương pháp.

Điều này rất hữu ích, ví dụ, để tạo một lớp con trỏ thông minh, đóng gói con trỏ thực. Toán tử bị quá tải-> được gọi, thực hiện bất cứ điều gì nó làm (ví dụ: khóa để đảm bảo an toàn luồng), trả về con trỏ bên trong và sau đó trình biên dịch gọi -> cho con trỏ bên trong này.

Đối với constness - nó đã được trả lời trong các bình luận và các câu trả lời khác (bạn có thể, và nên, cung cấp cả hai).

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.