2008-09-24 41 views
26

Tôi đang cố chuyển một hàm thành viên trong một lớp tới một hàm nhận một con trỏ lớp hàm thành viên. Vấn đề tôi đang gặp là tôi không chắc chắn làm thế nào để làm điều này đúng cách trong lớp bằng cách sử dụng con trỏ này. Có ai có đề xuất?Làm thế nào để bạn vượt qua một con trỏ hàm thành viên?

Đây là một bản sao của lớp đó là đi qua các hàm thành viên:

class testMenu : public MenuScreen{ 
public: 

bool draw; 

MenuButton<testMenu> x; 

testMenu():MenuScreen("testMenu"){ 
    x.SetButton(100,100,TEXT("buttonNormal.png"),TEXT("buttonHover.png"),TEXT("buttonPressed.png"),100,40,&this->test2); 

    draw = false; 
} 

void test2(){ 
    draw = true; 
} 
}; 

Chức năng x.SetButton (...) được chứa trong một lớp học, trong đó "đối tượng" là một bản mẫu.

void SetButton(int xPos, int yPos, LPCWSTR normalFilePath, LPCWSTR hoverFilePath, LPCWSTR pressedFilePath, int Width, int Height, void (object::*ButtonFunc)()) { 

    BUTTON::SetButton(xPos, yPos, normalFilePath, hoverFilePath, pressedFilePath, Width, Height); 

    this->ButtonFunc = &ButtonFunc; 
} 

Nếu có ai có lời khuyên nào về cách tôi có thể gửi đúng chức năng này để tôi có thể sử dụng sau này.

Trả lời

32

Để gọi hàm thành viên theo con trỏ, bạn cần hai thứ: Con trỏ trỏ đến đối tượng và con trỏ đến hàm. Bạn cần cả trong MenuButton::SetButton()

template <class object> 
void MenuButton::SetButton(int xPos, int yPos, LPCWSTR normalFilePath, 
     LPCWSTR hoverFilePath, LPCWSTR pressedFilePath, 
     int Width, int Height, object *ButtonObj, void (object::*ButtonFunc)()) 
{ 
    BUTTON::SetButton(xPos, yPos, normalFilePath, hoverFilePath, pressedFilePath, Width, Height); 

    this->ButtonObj = ButtonObj; 
    this->ButtonFunc = ButtonFunc; 
} 

Sau đó, bạn có thể gọi chức năng sử dụng cả hai con trỏ:

((ButtonObj)->*(ButtonFunc))(); 

Đừng quên để vượt qua con trỏ đến đối tượng của bạn để MenuButton::SetButton():

testMenu::testMenu() 
    :MenuScreen("testMenu") 
{ 
    x.SetButton(100,100,TEXT("buttonNormal.png"), TEXT("buttonHover.png"), 
     TEXT("buttonPressed.png"), 100, 40, this, test2); 
    draw = false; 
} 
+2

'Một con trỏ đến lớp' - đó có phải là' Con trỏ tới đối tượng' không? – Vorac

+0

Cảm ơn rất nhiều. Không thể tìm ra rằng cú pháp là một cái gì đó như ((ButtonObj) -> * (ButtonFunc))() – nathanesau

2

Trong trường hợp hiếm hoi mà bạn tình cờ được phát triển với Borland C++ Builder và không nhớ viết mã cụ thể với môi trường phát triển (có nghĩa là, mã mà sẽ không làm việc với khác Trình biên dịch C++), bạn có thể sử dụng từ khóa __closure. Tôi đã tìm thấy một số small article about C++Builder closures. Chúng được dự định chủ yếu để sử dụng với Borland VCL.

+2

Đó là một thay ... giải pháp tối nghĩa. –

+0

Yup, nó tối nghĩa. :) Có mã di sản Delphi và C++ Builder nổi ở đó trong đất công ty, mặc dù. Tốt nhất để được triệt để. – Parappa

5

Bạn sẽ không được phục vụ tốt hơn để sử dụng OO tiêu chuẩn. Xác định một hợp đồng (lớp ảo) và thực hiện điều đó trong lớp của riêng bạn, sau đó chỉ cần chuyển một tham chiếu đến lớp của riêng bạn và để cho người nhận gọi hàm hợp đồng.

Sử dụng ví dụ của bạn (tôi đã đổi tên thành phương pháp 'test2' thành 'buttonAction'):

class ButtonContract 
{ 
    public: 
    virtual void buttonAction(); 
} 


class testMenu : public MenuScreen, public virtual ButtonContract 
{ 
    public: 
    bool draw; 
    MenuButton<testMenu> x; 

    testMenu():MenuScreen("testMenu") 
    { 
     x.SetButton(100,100,TEXT("buttonNormal.png"), 
       TEXT("buttonHover.png"), 
       TEXT("buttonPressed.png"), 
       100, 40, &this); 
     draw = false; 
    } 

    //Implementation of the ButtonContract method! 
    void buttonAction() 
    { 
     draw = true; 
    } 
}; 

Trong phương thức thu, bạn lưu trữ các tham chiếu đến một ButtonContract, sau đó khi bạn muốn thực hiện các hành động của nút chỉ cần gọi phương thức 'buttonAction' của đối tượng ButtonContract đã lưu.

+0

Vấn đề là số lượng tuyệt đối của các chức năng khác nhau có thể được gọi bằng một nút, sẽ làm cho công việc này rất tẻ nhạt. Có thể có từ 75 đến 100 chức năng mà một nút có thể gọi. –

+0

Một testMenu * hasA * ButtonContract (hoặc nhiều người trong số họ), chứ không phải là một mối quan hệ * isA *. Cách tiếp cận này làm cho khó thực hiện hai nút với cùng một hành động hoặc các nút có các hành động khác nhau. – mabraham

2

Những người khác đã cho bạn biết cách thực hiện chính xác. Nhưng tôi ngạc nhiên khi không ai nói với bạn mã này thực sự nguy hiểm:

this->ButtonFunc = &ButtonFunc; 

Vì ButtonFunc là một tham số, nó sẽ nằm ngoài phạm vi khi hàm trả về. Bạn đang lấy địa chỉ của nó. Bạn sẽ nhận được một giá trị kiểu void (object::**ButtonFunc)() (con trỏ tới con trỏ tới hàm thành viên) và gán cho nó-> ButtonFunc. Tại thời điểm bạn sẽ cố gắng sử dụng này-> ButtonFunc bạn sẽ cố gắng truy cập vào lưu trữ của (bây giờ không tồn tại nữa) tham số địa phương, và chương trình của bạn có thể sẽ sụp đổ.

Tôi đồng ý với giải pháp của Commodore.Nhưng bạn phải thay đổi dòng của mình để

((ButtonObj)->*(ButtonFunc))(); 

từ ButtonObj là một con trỏ để phản đối.

6

Tôi biết đây là chủ đề khá cũ. Nhưng có một cách thanh lịch để xử lý này với C++ 11

#include <functional> 

tuyên bố con trỏ chức năng của bạn như thế này

typedef std::function<int(int,int) > Max; 

khai báo chức năng của bạn vượt qua bạn điều này vào

void SetHandler(Max Handler); 

giả sử bạn chuyển một hàm bình thường cho nó, bạn có thể sử dụng nó như bình thường

SetHandler(&some function); 

giả sử bạn có một hàm thành viên

class test{ 
public: 
    int GetMax(int a, int b); 
... 
} 

trong mã của bạn, bạn có thể vượt qua nó bằng cách sử std::placeholders như thế này

test t; 
Max Handler = std::bind(&test::GetMax,&t,std::placeholders::_1,std::placeholders::_2); 
some object.SetHandler(Handler); 
+1

Cảm ơn vì điều này. Bạn đã có một loại mặc dù: đó là 'std :: placeholders', số nhiều. – Artfunkel

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