2015-06-10 40 views
5

Tôi không hiểu điều gì sai với mã này. Nó trông giống như một cái bẫy đáng kinh ngạc!Hành vi lạ khi gọi các chức năng ảo

Mã này:

class Foo 
{ 
    public: 
     virtual double foo(double x) const = 0; 
       double foo(int x) const { return (double)(x + x); } 
}; 

class Bar : public Foo 
{ 
    public: 
     virtual double foo(double x) const { return x * x; } 
}; 

int main() 
{ 
    Bar* b = new Bar; 
    Foo* f = b; 
    std::cout << b->foo(3) << " " << f->foo(3) << std::endl; 
    std::cout << b->foo(5.0) << " " << f->foo(5.0) << std::endl; 
    return 0; 
} 

in đầu ra sau đây:

9 6 
25 25 

Tôi suy luận rằng Bar::foo(double) const được gọi với một diễn viên tiềm ẩn khi kiểu của con trỏ là Bar*. Nhưng lý do tại sao một điều như vậy là có thể mà không có bất kỳ cảnh báo nào?

Tôi làm việc với GCC 4.7.2. Tôi đã biên soạn với g++ -Wall foobar.cpp -o foobar.exe

+2

Tôi nghĩ rằng nó sẽ tốt hơn nếu bạn chọn số thứ hai của bạn khác hơn 2. Bởi vì 2 * 2 = 4 và 2 + 2 = 4. – nabroyan

+1

Tôi tò mò, bạn có cùng một vấn đề nếu giá trị trả về của 'foo (int)' cũng là 'int'? @nabroyan thực sự, nó sẽ là tốt hơn để sử dụng cùng một giá trị (nhưng loại khác nhau): '3' và' 3.0'. – Kryptos

+1

Bản sao có thể có của http://stackoverflow.com/questions/1628768/why-does-an-overridden-function-in-the-derived-class-hide-other-overloads-of-the và http: // stackoverflow. com/questions/411103/function-with-same-name-but-different-signature-in-derived-class – Kryptos

Trả lời

9

Điều này là do tên lẩn trốn.

Khi bạn khai báo hàm có tên foo trong Bar, bạn ẩn tất cả các khai báo có cùng tên trong Foo.

Như vậy, khi loại tĩnh của con trỏ là Bar, trình biên dịch chỉ tìm phiên bản trong Bar mà phải mất một double, vì vậy nó mặc nhiên chuyển đổi int để đáp ứng này.

Nếu bạn muốn phiên bản int trong Foo để được nhìn thấy, thêm một tuyên bố using:

class Bar : public Foo 
{ 
    public: 
     using Foo::foo; 
//  ^^ makes the versions in Foo visible 
     virtual double foo(double x) const { return x * x; } 
}; 
2

Khi loại là Bar*, chỉ có một phiên bản của phương pháp hiển thị, một phiên bản có thông số double.

Phương thức cơ sở có cùng tên (nhưng chữ ký khác) bị ẩn.

Để làm cho chúng khả dụng, bạn có thể sử dụng using Foo::foo trong lớp dẫn xuất.

Tùy thuộc vào trình biên dịch của bạn, tôi nghĩ bạn cũng có thể nhận được cảnh báo về chuyển đổi tiềm ẩn hoặc thực tế là bạn dường như muốn gọi một phương thức ẩn.

1

Trong Foo có hai tình trạng quá tải foo, một quá trình mất double và cách khác mất int.

Trong Bar có một quá tải là foo, số điện thoại mất double. Quá tải này ẩn tất cả các hàm có cùng tên từ các lớp cơ sở. Tên này được gọi là tên ẩn.

Một sửa chữa sẽ được sử dụng sử dụng khai đưa foo quá tải khác từ lớp cơ sở trong phạm vi Foo lớp có nguồn gốc:

class Bar : public Foo 
{ 
    public: 
     using Foo::foo; 
     virtual double foo(double x) const { return x * x; } 
}; 
Các vấn đề liên quan