Hàm có cùng tên nhưng khác chữ ký trong lớp dẫn xuất


90

Tôi có một hàm có cùng tên, nhưng có chữ ký khác trong một cơ sở và các lớp dẫn xuất. Khi tôi đang cố gắng sử dụng hàm của lớp cơ sở trong một lớp khác kế thừa từ lớp dẫn xuất, tôi nhận được lỗi. Xem đoạn mã sau:

class A
{
    public:
    void foo(string s){};
};

class B : public A
{
    public:
    int foo(int i){};
};

class C : public B
{
    public:
    void bar()
    {
        string s;
        foo(s);
    }
};

Tôi nhận được lỗi sau từ trình biên dịch gcc:

In member function `void C::bar()': no matching function for call to `C::foo(std::string&)' candidates are: int B::foo(int)

Nếu tôi xóa int foo(int i){};khỏi lớp học Bhoặc nếu tôi đổi tên nó từ đó foo1, mọi thứ đều hoạt động tốt.

Vấn đề với điều này là gì?


1
Về mặt kỹ thuật là một bản sao của câu hỏi này nhưng câu này có tiêu đề và câu trả lời hay hơn.
Troubadour

Câu trả lời:


77

Các hàm trong lớp dẫn xuất không ghi đè các hàm trong lớp cơ sở nhưng có cùng tên sẽ ẩn các hàm khác cùng tên trong lớp cơ sở.

Thông thường, việc có các hàm trong các lớp dẫn xuất có cùng tên với các hàm trong lớp bass không nhằm mục đích ghi đè các hàm của lớp cơ sở vì những gì bạn đang thấy thường không phải là hành vi mong muốn. Thường nên đặt các tên khác nhau cho các hàm khác nhau.

Nếu bạn cần gọi hàm cơ sở, bạn sẽ cần xác định phạm vi cuộc gọi bằng cách sử dụng A::foo(s). Lưu ý rằng điều này cũng sẽ vô hiệu hóa bất kỳ cơ chế chức năng ảo nào A::foo(string)cùng một lúc.


13
cũng đọc câu trả lời của litdb: bạn có thể 'bỏ ẩn' hàm cơ sở bằng mệnh đề 'using A :: foo' trong B.
xtofl

Đúng vậy, tôi chỉ đang xem xét một giải pháp có thể được sử dụng tại trang web cuộc gọi, coi phân cấp cơ sở là cố định.
CB Bailey

2
Cơ sở của tuyên bố này là gì và theo sau là lời khuyên: "Thông thường, việc có các hàm trong các lớp dẫn xuất có cùng tên với các hàm trong lớp trầm không nhằm mục đích ghi đè các hàm của lớp cơ sở như những gì bạn đang thấy thường không phải là hành vi mong muốn. Bạn nên đặt tên khác cho các hàm khác nhau " . Điều gì sẽ xảy ra nếu chúng đang làm điều tương tự về mặt ngữ nghĩa? Tuy nhiên, C ++ cung cấp cho bạn giải pháp cho vấn đề do điều này gây ra, như câu trả lời của Johannes đã giải thích.
Nawaz

107

Đó là bởi vì việc tra cứu tên sẽ dừng lại nếu nó tìm thấy tên ở một trong các cơ sở của bạn. Nó sẽ không nhìn xa hơn ở các căn cứ khác. Hàm trong B phủ bóng hàm trong A. Bạn phải khai báo lại hàm của A trong phạm vi của B, để cả hai hàm đều hiển thị từ bên trong B và C:

class A
{
    public:
    void foo(string s){};
};

class B : public A
{
    public:
    int foo(int i){};
    using A::foo;
};

class C : public B
{
    public:
    void bar()
    {
        string s;
        foo(s);
    }
};

Chỉnh sửa: Mô tả thực mà Tiêu chuẩn đưa ra là (từ 10,2 / 2):

Các bước sau đây xác định kết quả tra cứu tên trong phạm vi lớp, C. Đầu tiên, mọi khai báo cho tên trong lớp và trong mỗi đối tượng con của lớp cơ sở của nó đều được xem xét. Tên thành viên f trong một đối tượng con B ẩn tên thành viên f trong đối tượng con A nếu A là đối tượng con lớp cơ sở của B. Bất kỳ khai báo nào bị ẩn như vậy đều bị loại khỏi việc xem xét. Mỗi khai báo này được đưa vào bởi một khai báo sử dụng được coi là từ mỗi đối tượng con của C thuộc kiểu chứa khai báo được chỉ định bởi khai báo sử dụng.96) Nếu tập hợp khai báo kết quả không tất cả từ các đối tượng con cùng loại, hoặc tập hợp có thành viên không ổn định và bao gồm các thành viên từ các đối tượng con riêng biệt, sẽ có sự mơ hồ và chương trình không được định hình. Nếu không thì tập hợp đó là kết quả của việc tra cứu.

Nó có những điều sau đây để nói ở một nơi khác (ngay phía trên nó):

Đối với biểu thức id [ một cái gì đó như "foo" ], tra cứu tên bắt đầu trong phạm vi lớp của cái này; đối với id đủ điều kiện [ một cái gì đó như "A :: foo", A là mã định danh lồng nhau ], tra cứu tên bắt đầu trong phạm vi của mã định danh tên lồng nhau. Việc tra cứu tên diễn ra trước khi kiểm soát truy cập (3.4, khoản 11).

([...] do tôi đặt). Lưu ý rằng điều đó có nghĩa là ngay cả khi foo của bạn trong B là riêng tư, foo trong A vẫn sẽ không được tìm thấy (vì kiểm soát truy cập xảy ra sau đó).


litb, cảm ơn câu trả lời của bạn. Nhưng khi tôi cố gắng biên dịch mã của bạn, tôi nhận được: không thể điều chỉnh quyền truy cập vào void A::foo(class basic_string<char,char_traits<char>,allocator<char> >)' in lớp B 'vì phương thức cục bộ `int B :: foo (int)' có cùng tên. Có lẽ đó là vì tôi sử dụng một phiên bản cũ của gcc
Igor Oks

1
vâng, chắc chắn là một lỗi trình biên dịch. các trình biên dịch cũ thường sử dụng "A :: foo;" thay vì "using A :: foo;" nhưng trước đây không được chấp nhận trong C ++.
Johannes Schaub - litb
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.