2010-11-10 51 views
20

Tôi có các lớp sau:Tại sao chức năng ảo bị ẩn?

class A { 
public: 
    virtual void f() {} 
}; 


class B : public A{ 
public: 
    void f(int x) {} 
}; 

Nếu tôi nói

B *b = new B(); 
b->f(); 

trình biên dịch nói lỗi C2660: 'B :: f': chức năng không mất 0 đối số. Không nên chức năng trong B quá tải nó, vì nó là một chức năng ảo? Các chức năng ảo có bị ẩn như thế này không?

EDIT: Tôi thực sự muốn kế thừa B từ A, cho thấy hành vi tương tự.

+9

Có lẽ bạn muốn lấy 'b' từ' A'? –

Trả lời

35

Giả sử bạn dự định B để lấy được từ A:

f(int)f() là chữ ký khác nhau, do đó chức năng khác nhau.

Bạn có thể ghi đè chức năng ảo với chức năng có chữ ký tương thích, có nghĩa là chữ ký giống hệt hoặc chữ ký trong đó loại trả về "cụ thể hơn" (hiệp phương sai).

Nếu không, hàm lớp dẫn xuất của bạn ẩn hàm ảo, giống như bất kỳ trường hợp nào khác trong đó lớp dẫn xuất khai báo hàm có cùng tên với hàm lớp cơ sở. Bạn có thể đặt using A::f; vào lớp B để bỏ ẩn tên

Hoặc bạn có thể gọi nó là (static_cast<A*>(b))->f(); hoặc làm b->A::f();. Sự khác biệt là nếu B thực sự ghi đè lên f(), thì trước đây gọi là ghi đè, trong khi lệnh sau gọi hàm trong A bất kể.

+0

Làm rõ điều đó! Tôi không biết tại sao tôi lại nghĩ rằng khi sử dụng 'ảo' thì không có ẩn nấp nào xảy ra. Nhưng tôi thấy nó ngay bây giờ. Cảm ơn :) – Oszkar

+0

lý do tại sao chúng tôi không thể ẩn chức năng ảo với loại trả lại khác nhau? ngay cả các chức năng không phải ảo cũng cho phép loại trả về khác nhau. – UnKnown

+0

@UnKnown: Với chức năng * non * -virtual, các chức năng hoàn toàn riêng biệt; 'A * a = new B(); a.nonVirtualFunction() 'sẽ gọi' A :: nonVirtualFunction', không phải 'B :: nonVirtualFunction'. Nhưng với các hàm * virtual *, nó sẽ gọi 'B :: nonVirtualFunction', vì vậy rõ ràng' B :: nonVirtualFunction' phải ít nhất tương thích tối thiểu với 'A :: nonVirtualFunction'. – ruakh

7

Lớp B không lấy được từ A vì vậy không có hàm F() tồn tại. Bạn có thể có nghĩa là:

class A { 
public: 
    virtual void f() {} 
}; 


class B : public A { 
public: 
    void f(int x) {} 
}; 

Edit: tôi bị mất chức năng lẩn trốn thực tế. Xem Steve Jessop trả lời cho lời giải thích kỹ lưỡng hơn.

+0

Trong khi phát hiện tốt, nó không giải quyết được vấn đề (xem Steve Jessops trả lời). –

+0

LOL như @SteveJessop Tôi nhảy vào vấn đề chữ ký, thậm chí không nhận thấy 'B' không xuất phát từ 'A'! +1 – wilhelmtell

+0

@wilhelmtell: đúng, tôi không nhận thấy sự thừa kế còn thiếu cho đến khi Rod chỉ ra. –

2

B không xuất phát từ A, việc kê khai chính xác là:

class B : public A 
+0

Vâng, tôi quên điều đó, cảm ơn. – Oszkar

2

Khi trình biên dịch có nhiều hơn một cách để giải quyết một biểu tượng, nó phải chọn cái nào có độ ưu tiên trừ phi mã cho nó bằng cách khác . Những gì bạn đang mong đợi là quá tải để được ưu tiên hơn trọng tài. (hơn, hơn, hơn, aaaaack! Xin lỗi, bị 'quá sức').

Ví dụ này có B kế thừa phương thức ảo trong đó lớp con cung cấp phiên bản quá tải. Quá tải là cho các phương thức trong cùng một lớp bằng cách sử dụng cùng một tên phương thức nhưng có chữ ký khác nhau. Vì B là một phân lớp của A, nó ghi đè f(), điều đó có nghĩa là nó không thể là quá tải cùng một lúc. Đây là lý do tại sao nó đang được ẩn.

Đối với lớp A, phương pháp

virtual void f() {} 

làm phương tiện ảo phương pháp đó sẽ được giải quyết bằng một tập hợp các quy tắc mà không phù hợp với tuyên bố của bạn của b tuyên bố.

B *b = new B(); 

Bằng cách tạo 'b' làm trường hợp "B", trình biên dịch không cần sử dụng bản chất ảo của cùng một phương thức trong "A".

Nếu bạn đã tuyên bố 'b' như thế này

B *b = new A(); 

sau đó cuộc gọi b-> f(); thực sự sẽ đề cập đến phương pháp trong A bằng cách sử dụng độ phân giải ảo.

+0

Tôi quên viết nó theo cách đó. Tôi đã sửa nó ngay bây giờ. – Oszkar

4

Không, và có, tương ứng. Nếu bạn muốn các hành vi quá tải, bạn cần phải nói

using A::f; 

trong B.

+0

'B' không có nguồn gốc từ' A' –

+1

Thật vậy, tôi đã bỏ lỡ điều đó :) Mặc dù vậy, mặc dù vậy, đó là những gì bạn cần làm. –

+0

Vâng, tôi có nghĩa là phát sinh nó. Cảm ơn! – Oszkar

1

Dường như nó là tồn tại câu hỏi khá tương tự với câu trả lời trong FAQ Biern Stroustrup của: http://www.stroustrup.com/bs_faq2.html#overloadderived

Như ông nói:

"Trong C++, không có quá tải trên phạm vi"

nhưng nếu bạn muốn

"Đó là một cách dễ dàng thực hiện bằng cách sử dụng sử dụng-khai"

Các vấn đề liên quan