Đánh giá theo từ ngữ của câu hỏi của bạn (bạn đã sử dụng từ "ẩn"), bạn đã biết những gì đang xảy ra ở đây. Hiện tượng này được gọi là "giấu tên". Vì một số lý do, mỗi khi ai đó đặt câu hỏi về lý do tại sao việc ẩn tên xảy ra, những người trả lời lại nói rằng điều này được gọi là "ẩn tên" và giải thích cách hoạt động của nó (mà bạn có thể đã biết) hoặc giải thích cách ghi đè lên (mà bạn không bao giờ hỏi về), nhưng dường như không ai quan tâm để giải quyết câu hỏi "tại sao" thực sự.
Các quyết định, lý do đằng sau tên ẩn, tức là tại sao nó thực sự được thiết kế thành C ++, là để tránh một số hành vi phản trực giác, không lường trước và có khả năng nguy hiểm có thể xảy ra nếu tập hợp các hàm quá tải được thừa kế được phép trộn lẫn với tập hợp hiện tại của quá tải trong lớp nhất định. Bạn có thể biết rằng trong độ phân giải quá tải C ++ hoạt động bằng cách chọn chức năng tốt nhất từ nhóm ứng cử viên. Điều này được thực hiện bằng cách khớp các loại đối số với các loại tham số. Các quy tắc khớp đôi khi có thể phức tạp và thường dẫn đến kết quả có thể được coi là phi logic bởi người dùng không chuẩn bị. Thêm các chức năng mới vào một tập hợp các chức năng hiện có trước đây có thể dẫn đến một sự thay đổi khá mạnh mẽ trong kết quả giải quyết quá tải.
Ví dụ, giả sử lớp cơ sở B
có hàm thành viên foo
nhận tham số kiểu void *
và tất cả các lệnh gọi foo(NULL)
được giải quyết B::foo(void *)
. Giả sử không có tên ẩn và điều này B::foo(void *)
có thể nhìn thấy trong nhiều lớp khác nhau giảm dần B
. Tuy nhiên, giả sử trong một số hậu duệ [gián tiếp, từ xa] D
của lớp, B
một hàm foo(int)
được định nghĩa. Bây giờ, không có tên ẩn D
có cả foo(void *)
và foo(int)
có thể nhìn thấy và tham gia giải quyết quá tải. Hàm nào sẽ gọi để foo(NULL)
giải quyết, nếu được thực hiện thông qua một đối tượng thuộc loại D
? Họ sẽ giải quyết D::foo(int)
, vì int
là một kết hợp tốt hơn cho số không tích phân (nghĩa làNULL
) hơn bất kỳ loại con trỏ. Vì vậy, trong suốt các lệnh gọi phân cấp để foo(NULL)
giải quyết một chức năng, trong khi trongD
(và dưới), chúng đột nhiên giải quyết sang chức năng khác.
Một ví dụ khác được đưa ra trong Thiết kế và tiến hóa của C ++ , trang 77:
class Base {
int x;
public:
virtual void copy(Base* p) { x = p-> x; }
};
class Derived{
int xx;
public:
virtual void copy(Derived* p) { xx = p->xx; Base::copy(p); }
};
void f(Base a, Derived b)
{
a.copy(&b); // ok: copy Base part of b
b.copy(&a); // error: copy(Base*) is hidden by copy(Derived*)
}
Nếu không có quy tắc này, trạng thái của b sẽ được cập nhật một phần, dẫn đến cắt lát.
Hành vi này được coi là không mong muốn khi ngôn ngữ được thiết kế. Như một cách tiếp cận tốt hơn, nó đã được quyết định tuân theo đặc tả "ẩn tên", nghĩa là mỗi lớp bắt đầu bằng một "bảng sạch" đối với từng tên phương thức mà nó tuyên bố. Để ghi đè hành vi này, một hành động rõ ràng được yêu cầu từ người dùng: ban đầu là khai báo lại (các) phương thức được kế thừa (hiện không dùng nữa), giờ đây sử dụng khai báo rõ ràng.
Như bạn đã quan sát chính xác trong bài viết gốc của mình (tôi đang đề cập đến nhận xét "Không đa hình"), hành vi này có thể được coi là vi phạm quan hệ IS-A giữa các lớp. Điều này là đúng, nhưng rõ ràng hồi đó người ta đã quyết định rằng việc giấu tên cuối cùng sẽ chứng tỏ là một kẻ ác ít hơn.