2010-06-23 38 views
7

Hãy xem xét đoạn mã sau:quá tải ảo chức năng cuộc gọi có độ phân giải

class Abase{}; 
class A1:public Abase{}; 
class A2:public A1{}; 
//etc 

class Bbase{ 
public: 
    virtual void f(Abase* a); 
    virtual void f(A1* a); 
    virtual void f(A2* a); 
}; 

class B1:public Bbase{ 
public: 
    void f(A1* a); 
}; 

class B2:public Bbase{ 
public: 
    void f(A2* a); 
}; 

int main(){ 
    A1* a1=new A1(); 
    A2* a2=new A2(); 
    Bbase* b1=new B1(); 
    Bbase* b2=new B2(); 
    b1->f(a1); // calls B1::f(A1*), ok 
    b2->f(a2); // calls B2::f(A2*), ok 
    b2->f(a1); // calls Bbase::f(A1*), ok 
    b1->f(a2); // calls Bbase::f(A2*), no- want B1::f(A1*)! 
} 

Tôi muốn biết tại sao C++ chọn để giải quyết các cuộc gọi chức năng trên dòng cuối cùng bởi upcasting con trỏ this của đối tượng vào cơ sở lớp học, thay vì upcasting các đối số của f()? Có cách nào để tôi có thể có được hành vi mà tôi muốn không?

Trả lời

10

Lựa chọn phiên bản f để thực hiện cuộc gọi bằng cách xem loại tham số biên dịch. Loại thời gian chạy không được xem xét cho độ phân giải tên này. Vì b1 thuộc loại Bbase*, tất cả các thành viên của Bbase đều được xem xét; một trong đó có một A2* là trận đấu hay nhất, vì vậy đó là một trong đó được gọi là.

+0

Cảm ơn - như tôi đã hiểu, vấn đề là độ phân giải mà lệnh gọi f() ảo xảy ra tại thời gian biên dịch, dựa trên đối số được cung cấp cho f(). Vì vậy, trên dòng cuối cùng, trình biên dịch đã quyết định rằng f (A2 *) sẽ được gọi. Phiên bản của f (A2 *) được gọi sau đó phụ thuộc vào loại thời gian chạy được trỏ đến. Ở đây, vì lớp B1 không vượt quá f (A2 *), nên phiên bản lớp cơ sở được gọi. – stw

+0

@stw, hoàn toàn chính xác. –

1
b1->f(static_cast<A1*>(a2)); 

Điều này sẽ buộc trình biên dịch sử dụng phương thức quá tải với thông số loại A1.

+0

một dynamic_cast là thích hợp hơn, tôi nghĩ vậy. – Jason

+2

@ Jason: Một 'static_cast' đến một lớp cơ sở là tốt. –

+2

Tôi nghĩ rằng một diễn viên tĩnh là OK, như chúng ta a) biết rằng diễn viên là chính xác và b) nó là một upcast, đó là an toàn. –

0

Nó được gọi là ẩn tên. Mỗi f bạn khai báo trong một lớp có nguồn gốc bóng mỗi f có thể có trong bất kỳ lớp cơ sở của nó.

Sử dụng dàn diễn viên vào lớp cơ sở để có được hành vi mong muốn.

Khi bạn ghi đè hàm ảo, bạn không ghi đè lên các hàm quá tải có cùng tên. Chúng là các hàm khác nhau (và có các mục khác nhau trong vtable).

+0

Không, vấn đề ở đây là cách khác. Nó sẽ được giấu tên nếu bạn gọi thông qua một con trỏ đến một lớp có nguồn gốc nhiều hơn mà ẩn (các) phương thức từ lớp cơ sở. –

+0

ooops bạn nói đúng, tôi đọc quá nhanh. –

2

"... chọn để giải quyết cuộc gọi hàm trên dòng cuối cùng bằng cách chuyển con trỏ của đối tượng này sang lớp cơ sở ...". Bạn đang nói về cái gì vậy? Trong tất cả các cuộc gọi của bạn, kiểu con trỏ đối tượng là Bbase * và các chức năng mà các cuộc gọi quyết định thuộc về Bbase hoặc con cháu của nó. Trình biên dịch không bao giờ thực hiện bất kỳ upcasting nào để giải quyết các cuộc gọi của bạn. Thực tế, hai cuộc gọi đầu tiên yêu cầu downcasting để gọi người ghi đè thích hợp, vì người ghi danh thuộc về lớp nằm sâu hơn trong hệ thống phân cấp. Đối với hai cuộc gọi cuối cùng - chúng được gửi vào lớp Bbase thông qua con trỏ loại Bbase *. Các loại khớp chính xác, không có loại truyền nào xảy ra.

Đối với độ phân giải quá tải ... Độ phân giải quá tải là quá trình biên dịch thời gian, dựa trên các loại tĩnh của đối số và xếp hạng của các chuyển đổi có thể. Bạn đã cung cấp một đối số của loại A2 *. Ứng cử viên f(A2 *) khớp với đối số của bạn chính xác. Ứng cử viên f(A1 *) yêu cầu chuyển đổi thêm từ A2 * thành A1 *. Các ứng cử viên phù hợp chính xác được coi là một tốt hơn, do đó, nó thắng độ phân giải quá tải. Đơn giản.

+0

Xin lỗi- Tôi đã sử dụng cụm từ 'upcasting' theo cách sai. Ý tôi là tại sao đối tượng được coi như một BBase ở đây. Câu trả lời (như bạn nói) là độ phân giải quá tải xảy ra tại thời gian biên dịch, và tại thời gian biên dịch đối tượng là một BBase, do đó trình biên dịch chọn f (A2 *). – stw

0

Quá tải của bạn trong Bbase cho Abase và A2 được ẩn trong B1. lẽ bạn có thể làm việc xung quanh vấn đề đó như thế này:

class Bbase{ 
public: 
    inline void f(Abase* a) { f_(a); } 
    inline void f(A1* a) { f_(a); } 
    inline void f(A2* a) { f_(a); } 
protected: 
    virtual void f_(Abase* a); 
    virtual void f_(A1* a); 
    virtual void f_(A2* a); 
}; 

class B1:public Bbase{ 
protected: 
    void f_(A1* a); 
}; 

class B2:public Bbase{ 
protected: 
    void f_(A2* a); 
}; 

hoặc với một mẫu trong Bbase:

class Bbase{ 
public: 
    template<class myA> 
    inline void f(myA* a) { f_(a); } 
protected: 
    virtual void f_(Abase* a); 
    virtual void f_(A1* a); 
    virtual void f_(A2* a); 
}; 
Các vấn đề liên quan