2015-05-21 188 views
3

我有很多類X的實例,它們每個包含另一個類CB(有時不止一個CB)。 CB類有一個函數指針,它將在某個事件(點擊,計時器等)之後被調用。傳遞一個非靜態成員函數作爲參數

這將是非常有用,如果我可以只通過一個實例成員函數當地CB類實例(通過在這種情況下say()功能),這樣一旦CB觸發功能,它會修改的東西在自己的實例只。

實施例的示例代碼:

#include <iostream>                          

class CB {                         
    void (*function)(void);                        

    void init(void (*function)(void)) {                     
     this->function = function;                      
    }                             
};                              

class X {                            
    private:                            
     CB cb;                           

    public:                            
     void init() {                         
      cb.init(&(this->say)); //<- Here's the problem                
     }                            

     void say() {                          
      std::cout << "Hello World!" << std::endl;                 
     }                            
};                              

int main() {                            
    X x;                             
    x.init();                           
} 

所以我的上述例子中由於失敗線17與錯誤:

no known conversion for argument 1 from ‘void (X::*)()’ to ‘void (*)()’ 

是否有可能以某種方式通過一個實例地方say()功能實例CBCB函數不能被修改,不幸的是會被其他類型的其他類型使用。

我讀過其他線程,這很可能是不可能的,除非say()函數是static或存在於類外(這意味着它不會訪問實例變量)。

是否有可能按照我擁有的方式工作?如果不是最常見的解決方案(這似乎是很多人會遇到的問題)?

+0

你知道如何編寫構造函數嗎? –

+0

當'CB'調用試圖修改其狀態的方法時,最好希望你的'X'實例仍然存在。 – CoryKramer

+0

爲什麼你需要一個函數指針?改用虛擬功能(接口)。 –

回答

0

這裏有一種方法得到的東西至少隱約與此類似,但仍然可以工作:

#include <iostream> 
#include <functional> 

class CB 
{ 
public: 
    std::function<void(void)> function; 

    template <class F> 
    CB(F function) : function(function) { } 

    void operator()() { function(); } 
}; 

class X 
{ 
private: 
    CB cb; 

public: 
    X() :cb([this] {say(); }) { } 

    void say() { std::cout << "Hello World!\n"; } 

    void operator()() { cb(); } 
}; 

int main() { 
    X x; 
    x(); 
} 

在這裏,我重寫CB一點點。其中一些與現有代碼不兼容,但這是因爲現有設計不是一個好主意。現在你正在做的是通常所說的兩階段初始化,在那個對象真的可以使用之前,你必須創建一個對象,調用它的init()。這通常是皺眉。

我也改變了CB存儲的std::function,它可以用來存儲指向一個功能,幾乎什麼都可以調用的功能等。

然後,我添加了一個operator()來執行存儲的函數式對象的實際調用。

X我做了一些改變,最大的是使用lambda表達式來產生一個可以像函數一樣調用的對象。這是在施工期間傳遞給CB的嵌入式實例。

所以順序是外部代碼創建一個X對象。這會創建CB類的嵌入式實例,並向其傳遞一個調用X::say的閉包。然後我們調用X::operator(),調用CB::operator(),調用X::say()

+0

目前還不清楚'class CB'實際上是在添加什麼值......看起來,人們可以擺脫它並使用'std :: function cb;' –

+0

@BenVoigt:很可能 - 我把它留在,猜測在現實生活中它可能會做更多/其他的事情。 –

0

"Is it possible to somehow pass an instances local say() function to the instances CB ?

如果你確定你需要一個函數指針,使用std::function包裝代替:

class CB { 
    // void (*function)(void); Replace this with: 
    std::function<void,void> fn_; 
public:                     
    // void init(void (*function)(void)) { 
    //  this->function = function; 
    // } Replace with a constructor 
    CB(const std::function<void,void>& fn) : fn_(fn) {} 
}; 

如果你需要將其綁定到一個特定的類實例使用std::bind

class X { 
    CB cb_; 
public: 
    // Again use a constructor with a member initializer list 
    void X() : cb_(std::bind(&X::say, this) {} 

    void say() { 
     std::cout << "Hello World!" << std::endl; 
    } 
}; 

"The CB function can't be modified and unfortunately will be used by other classes of a different type."
"I've read in other threads that this is most likely impossible unless the say() function is static or exists out side the class (which means it won't have access to an instances variables)."

如果是這樣,你有點失落了。由於回調函數簽名不提供一種用戶數據參數,因此不可能提供包含對特定實例的引用的包裝回調提供程序。

是否在您的真正的代碼(我想你已經在你的問題簡化IT)真有void fn(void);簽名,或者它提供了一些參數的回調函數,即允許其結合到像void fn(void* userData);任何數據實例?


您可能仍然能夠提供的class X一個static成員函數作爲回調。如果say()功能不改變狀態的X(如您的樣品中),將其更改爲static實際上不是一個問題(但恐怕這不是你在這裏後在做什麼):

class CB { 
    void (*fn_)(void); 
public: 
    CB(void (*fn)(void)) : fn_(fn) {} 
}; 

class X { 
    CB cb_; 
public: 
    // Again use a constructor with a member initializer list 
    void X() : cb_(&X::say) {} 

    static void say() { 
// ^^^^^^ 
     std::cout << "Hello World!" << std::endl; 
    } 
}; 
相關問題