2010-11-25 49 views
9

Giả sử tôi có một kiểu con trỏ có thể chứa địa chỉ của một phương thức lớp cơ sở. Tôi có thể gán địa chỉ của một phương thức phân lớp cho nó và mong nó hoạt động chính xác không? Trong trường hợp của tôi, tôi đang sử dụng nó với một con trỏ lớp cơ sở và kiểu động của đối tượng là lớp dẫn xuất.Có an toàn để "upcast" một con trỏ phương pháp và sử dụng nó với con trỏ lớp cơ sở?

struct B 
{ 
    typedef void (B::*MethodPtr)(); 
}; 

struct D: public B 
{ 
    void foo() { cout<<"foo"<<endl; } 
}; 

int main(int argc, char* argv[]) 
{ 
    D d; 
    B* pb = &d; 

    //is the following ok, or undefined behavior? 
    B::MethodPtr mp = static_cast<B::MethodPtr>(&D::foo); 
    (pb->*mp)(); 
} 

Tiêu chuẩn nói điều này khi nói về static_cast:

5.2.9.9 Một rvalue kiểu “con trỏ tới thành viên của D loại CV1 T” có thể được chuyển đổi sang một rvalue kiểu “con trỏ thành phần B của loại cv2 T ”, trong đó B là một lớp cơ sở (điều 10) của D, nếu chuyển đổi chuẩn hợp lệ từ" con trỏ thành thành viên B loại T "thành" con trỏ thành thành viên D của kiểu T " tồn tại (4.11), và cv2 là cùng mức độ cv, hoặc bằng cv lớn hơn, cv1. 63) Giá trị con trỏ thành viên null (4.11) được chuyển thành giá trị con trỏ thành viên null của kiểu đích. Nếu lớp B chứa thành viên ban đầu, hoặc là một lớp cơ sở hoặc dẫn xuất của lớp có chứa thành viên ban đầu, thì con trỏ kết quả đến các thành viên trỏ đến thành viên ban đầu. Nếu không, kết quả của diễn viên là không xác định. [Lưu ý: mặc dù lớp B cần không chứa thành viên ban đầu, loại động của đối tượng mà con trỏ tới thành viên bị hủy đăng ký phải chứa thành viên ban đầu; xem 5.5.]

Như mọi khi, tôi có một thời gian khó giải mã tiêu chuẩn. Nó kinda nói rằng nó là ok, nhưng tôi không chắc chắn 100% nếu các văn bản trên thực sự áp dụng cho tình hình trong mã ví dụ của tôi.

+0

tại sao bạn không thể sử dụng chức năng ghi đè chức năng ảo thông thường? – YeenFei

+0

mã hóa hơi giống như đi bộ trên một con đường núi, đi bộ ở giữa đường là an toàn hơn so với đi bộ gần vành, mã của bạn dường như gần với vành. :-) –

Trả lời

8

Hợp lệ.

Nếu lớp B chứa các thành viên ban đầu,

B không chứa D :: Foo, vì vậy không.

hoặc là một cơ sở [...] của lớp có chứa các thành viên ban đầu

B là một cơ sở của D, vì vậy đây nắm giữ. Kết quả là:

kết quả con trỏ đến điểm thành viên cho các thành viên ban đầu

khoản 5.2.9 9 nói rằng bạn có thể upCast chỉ khi bạn cũng có thể nhìn xuống, theo quy định tại § 4.11:

Giá trị của loại "con trỏ tới thành viên B của loại cv T", trong đó B là loại lớp, có thể được chuyển đổi thành giá trị của loại "con trỏ thành thành viên D của loại cv T", trong đó D là một lớp dẫn xuất (điều 10) của B. Nếu B là một lớp không thể tiếp cận (điều 11), không rõ ràng (10.2) hoặc lớp cơ sở ảo (10.1) của D, một chương trình yêu cầu chuyển đổi này là hình thành không đúng.

Điều này chỉ cho biết bạn có thể downcast miễn là B có thể truy cập, không phải ảo và chỉ xuất hiện một lần trong biểu đồ kế thừa của D.

Sự nguy hiểm vốn có trong con trỏ phương pháp upcasting là bạn có thể gọi mp trên một đối tượng có loại thực tế là B. Miễn là khối mã giao dịch với D :: * cũng giao dịch với D *, bạn có thể tránh điều này.

+0

+1, nhưng tôi nghĩ câu cuối cùng hơi gây nhầm lẫn vì có một 'D *' không đảm bảo rằng kiểu động của đối tượng được trỏ tới thực sự là 'D' (hoặc một số kiểu bắt nguồn từ' D'), đó là tài sản bạn thực sự cần. –

+0

Vâng, tôi không quá hài lòng với cách diễn đạt, nhưng tôi đang gặp khó khăn trong việc đưa ra một quy tắc đủ rộng, chính xác và súc tích. Có lẽ viết "giới thiệu" thay vì "giao dịch"? – outis

+0

Làm thế nào về 'Miễn là một khối mã giao dịch với D :: * cũng giao dịch với một D * trỏ đến một đối tượng có kiểu động hoặc là D hoặc bắt nguồn từ D'? Tuy nhiên, có thể là quá dài dòng. –

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