2009-07-30 55 views
19

Tôi muốn gọi thực hiện lớp cơ sở của một hàm ảo bằng cách sử dụng con trỏ hàm thành viên.Gọi định nghĩa lớp cơ sở của hàm thành viên ảo với con trỏ hàm

class Base { 
public: 
    virtual void func() { cout << "base" << endl; } 
}; 

class Derived: public Base { 
public: 
    void func() { cout << "derived" << endl; } 

    void callFunc() 
    { 
     void (Base::*fp)() = &Base::func; 
     (this->*fp)(); // Derived::func will be called. 
         // In my application I store the pointer for later use, 
         // so I can't simply do Base::func(). 
    } 
}; 

Trong mã trên, việc triển khai lớp dẫn xuất của func sẽ được gọi từ callFunc. Có cách nào tôi có thể lưu một con trỏ hàm thành viên trỏ đến Base :: func, hoặc tôi sẽ phải sử dụng using theo một cách nào đó?

Trong ứng dụng thực tế, tôi sử dụng boost :: bind để tạo đối tượng hàm boost :: trong callFunc mà sau này tôi sử dụng để gọi func từ một phần khác của chương trình. Vì vậy, nếu tăng :: ràng buộc hoặc tăng :: chức năng có một số cách để có được xung quanh vấn đề này cũng sẽ giúp đỡ.

+0

Bản sao có thể có của [C++: Con trỏ tới phiên bản đơn hình của hàm thành viên ảo?] (Https://stackoverflow.com/questions/5064614/c-pointer-to-monomorphic-version-of-virtual-member-function) –

Trả lời

11

Khi bạn gọi một phương thức ảo thông qua tham chiếu hoặc con trỏ, bạn sẽ luôn kích hoạt cơ chế cuộc gọi ảo tìm loại có nguồn gốc cao nhất.

Đặt cược tốt nhất của bạn là thêm một chức năng thay thế không phải là ảo.

0

Có lý do cụ thể nào để thực hiện điều này thông qua một con trỏ hàm không?

Bạn sẽ có thể chỉ cần viết:

Base::func(); 

để gọi thực hiện lớp cơ sở.

+1

Như tôi đã viết trong câu hỏi của tôi, tôi lưu con trỏ trong callFunc nhưng sử dụng nó để thực sự gọi func từ một nơi khác trong chương trình của tôi. –

+0

Đối với một chức năng ảo, điều này sẽ không hoạt động. Nó sẽ luôn thực hiện tra cứu vtable và gọi phương thức trên lớp dẫn xuất. – Joel

1

Vấn đề của bạn là con trỏ hàm thành viên không hoàn toàn giống với con trỏ hàm trần. Nó thực sự không chỉ là một con trỏ, mà là một con số considerably more complex structure, thay đổi về chi tiết của nó ở mức độ thực thi trình biên dịch. Khi bạn gọi nó thông qua cú pháp (this->*fp)() bạn đang thực sự gọi nó trên đối tượng ban đầu, điều này gây ra việc gửi hàm ảo.

Một điều có thể hoạt động là truyền nội dung đó sang loại con trỏ không theo phương thức. Đây là một chút vệt nhưng tôi nghĩ rằng nó sẽ làm việc. Bạn vẫn cần phải vượt qua một Base * nhưng bạn làm điều đó một cách rõ ràng và các chức năng văn ảo là by-qua:

typedef void BasePointer(Base*); 

void callFunc() 
{ 
    BasePointer fp = (BasePointer *)&Base::func; 
    fp(this); 
} 

Cập nhật: Ok, không có, bạn không thể làm điều đó theo cách đó. Đó là bất hợp pháp, và sẽ không an toàn nếu nó là hợp pháp. C++ FAQmore on this. Nhưng biết điều đó không giải quyết được vấn đề của bạn. Vấn đề là, con trỏ đến đối tượng hoặc con trỏ thành viên nếu bạn muốn gọi số Base::func thông qua con trỏ Base, đối tượng trỏ nó phải là cũng làBase. Nếu bạn có thể sắp xếp, thì bạn có thể sử dụng con trỏ hàm thành viên.

Đây là một ý nghĩ khác, không đẹp, nhưng ít nhất là hoàn toàn khả thi. Cung cấp chức năng trong Derived, không ảo, gọi rõ ràng Base::func. Chỉ vào đó. Nó sẽ không mở rộng nếu bạn cần phải làm điều này trong trường hợp chung của rất nhiều biến thể khác nhau của funccallFunc nhưng nó sẽ hoạt động tốt cho một phương pháp.

+1

Điều đó chắc chắn sẽ không hoạt động! So sánh 'sizeof (& Base :: func)' với 'sizeof (BasePointer)': o –

+0

mmutz: Yup. Tôi cố gắng luôn kiểm tra trước khi đăng bài, và hôm nay tôi không để thời gian làm việc đó. Đó là * có thể * một 'static_cast' có thể đủ thông minh để bật phần con trỏ hàm ra khỏi con trỏ hàm thành viên, nhưng tôi e rằng khái niệm Python của hàm của tôi bây giờ là ghi đè C++ :) của tôi. – quark

+0

Điều này là sai: "nó mang theo nó không chỉ là địa chỉ của hàm để gọi mà còn là đối tượng để gọi nó trên" –

0

Ngoài những gì quark nói, một nhận xét chung hơn là bạn nên sử dụng một thực hiện tín hiệu/khe hơn là một con trỏ hàm trần. Boost có một, có libsigc và một loạt những người khác.

2

Điều bạn đang cố gắng thực hiện không thể thực hiện được. Các hàm con trỏ tới thành viên được thiết kế để duy trì tính ảo của hàm được chỉ định.

0

Có vấn đề gì với điều này?

(Base(*this).*fp)(); 

Bây giờ nếu bạn hài lòng với điều đó, nó đặt ra câu hỏi tại sao bạn thậm chí sử dụng con trỏ hàm ở địa điểm đầu tiên. Tôi nghĩ rằng một số bối cảnh nhiều hơn có thể giúp đỡ.

+0

Điều này đã được đề xuất bởi quark ("... nếu bạn muốn gọi Base :: func thông qua một con trỏ Base, đối tượng nó trỏ cũng phải là một cơ sở."). Tôi giả định rằng Eddie không thể đảm bảo rằng anh ta biết loại để cắt khi gọi chức năng. – Troubadour

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