2017-04-21 27 views
2

感謝您的支持。C++:如何存儲不同類的各種成員函數供以後使用

我正在使用C++ 11,我想存儲一些類的公共成員函數,以便稍後用作回調函數;例如我想存儲一些與此模板匹配的函數:void(classname :: *)(void)。據我所知,我也必須存儲它們的對象,沒關係。例如:

// PSEUDO CODE 
class A { 
    public: 
    void myfunc() {} 
}myobj; 

class B { 
    public: 
    void myfunc2() {} 
}myobj2; 

/* storing */ 
mystorageclass storage; 
storage.push(&myobj, &A::myfunc); 
storage.push(&myobj2, &B::myfunc2); 

/* call them back */ 
(storage[0].object->*(storage[0].callback))(); 
(storage[1].object->*(storage[1].callback))(); 

是否有任何安全和通用的方法來做到這一點?實際上我找到了一種方法,但我不確定它在整個處理器或編譯器中的可移植性。

//test.cpp - compiled with: g++ test.cpp -o test -std=c++11 
#include <iostream> 
#include <vector> 

class A { 
    public: 
    void myfunc() { std::cout << "Test A::myfunc()" << std::endl; } 
}myobj; 

class B { 
    public: 
    void myfunc2() { std::cout << "Test B::myfunc2()" << std::endl; } 
}myobj2; 

struct Callback { 
    void* object; 
    void(* method)(void*); 
}; 

std::vector<Callback> callbackList; 

template<typename FunctionPtr> 
void add(void* object, FunctionPtr fptr) { 
    Callback cb; 
    cb.object = object; 
    cb.method = (void(*)(void*))(*(void**)(&fptr)); 
    callbackList.push_back(cb); 
} 

int main() { 
    //add to list for later use 
    add(&myobj, &A::myfunc); 
    add(&myobj2, &B::myfunc2); 

    //call them back 
    callbackList[0].method(callbackList[0].object); 
    callbackList[1].method(callbackList[1].object); 
} 

而另一種方式;我覺得這樣更安全:

//test2.cpp - compiled with: g++ test2.cpp -o test2 -std=c++11 
#include <iostream> 
#include <vector> 

class A { 
    public: 
    void myfunc() { std::cout << "Test A::myfunc()" << std::endl; } 
}myobj; 

class B { 
    public: 
    void myfunc2() { std::cout << "Test B::myfunc2()" << std::endl; } 
}myobj2; 

struct Callback { 
    struct A; 
    A* object; 
    void(A::* method)(); 
    void call() { 
     (object->*method)(); 
    } 
}; 

std::vector<Callback> callbackList; 

template<typename FunctionPtr> 
void add(void* object, FunctionPtr fptr) { 
    Callback cb; 
    cb.object = (Callback::A*)object; 
    cb.method = (void(Callback::A::*)())(fptr); 
    callbackList.push_back(cb); 
} 

int main() { 
    //add to list for later use 
    add(&myobj, &A::myfunc); 
    add(&myobj2, &B::myfunc2); 

    //call them back 
    callbackList[0].call(); 
    callbackList[1].call(); 
} 

這些用法是否安全?或者你建議,而不是這些。 謝謝。

+4

使用['標準:: function']( http://en.cppreference.com/w/cpp/utility/functional/function)。 –

+1

你有沒有試過編譯第二個版本?它不應該編譯。你不能使用'void *'調用成員函數。 –

+0

我試過了,但這些都是我得到的最好結果。 @CaptainObvlious –

回答

3

問題與第二個版本

在行

cb.method = (void(*)(void*))(*(void**)(&fptr)); 

你鑄造函數指針void**。我不確定這是由標準支持。我的猜測是不是。我知道鑄造一個函數指針void*不被標準支持。有關詳細信息,請參閱Print an address of function in C++, g++/clang++ vs vc++ , who is rght?

,然後你繼續使用:

callbackList[1].method(callbackList[1].object); 

這依賴於調用類的成員函數時所使用的編譯器來傳遞this作爲第一個隱藏參數約定。不保證所有編譯器都使用該方法。該標準沒有明確說明。

問題的第三個/最後版本

您正在使用:

cb.object = (Callback::A*)object; 
cb.method = (void(Callback::A::*)())(fptr); 

不管對象類型是否爲AB。這是未定義行爲的原因。該標準不支持將對象指針轉換爲任何舊的指針類型。

清潔型版本

使用一個基類Callback

struct Callback { 
    virtual ~Callback() = 0; 
    virtual void call() = 0; 
}; 

然後,使用類模板爲真正的Callback秒。

template <typename T> 
struct RealCallback : public Callback 
{ 
    RealCallback(T* obj, void (T::*m)(void)) : object(obj), method(m) {} 

    virtual void call() 
    { 
     (object->*method)(); 
    } 

    T* object; 
    void (T::*method)(); 
}; 

有了這個,你將無法保存Callback對象的列表,但你可以存儲shared_ptr<Callback>的List。

std::vector<std::shared_ptr<Callback>> callbackList; 

下面是一個不依賴於任何難看管型和完美的作品一個完整的程序。

//test.cpp - compiled with: g++ test.cpp -o test -std=c++11 
#include <iostream> 
#include <vector> 
#include <memory> 

class A { 
    public: 
    void myfunc() { std::cout << "Test A::myfunc() on " << this << std::endl; } 
}myobj; 

class B { 
    public: 
    void myfunc2() { std::cout << "Test B::myfunc2() on " << this << std::endl; } 
}myobj2; 

struct Callback { 
    virtual void call() = 0; 
}; 

template <typename T> 
struct RealCallback : public Callback 
{ 
    RealCallback(T* obj, void (T::*m)(void)) : object(obj), method(m) {} 

    virtual void call() 
    { 
     (object->*method)(); 
    } 

    T* object; 
    void (T::*method)(); 
}; 

std::vector<std::shared_ptr<Callback>> callbackList; 

template<typename T> 
void add(T* object, void (T::*fptr)()) { 
    RealCallback<T>* cb = new RealCallback<T>(object, fptr); 
    callbackList.push_back(std::shared_ptr<Callback>(cb)); 
} 

int main() { 
    //add to list for later use 
    add(&myobj, &A::myfunc); 
    add(&myobj2, &B::myfunc2); 

    std::cout << "myobj: " << &myobj << std::endl; 
    std::cout << "myobj2: " << &myobj2 << std::endl; 

    //call them back 
    callbackList[0]->call(); 
    callbackList[1]->call(); 
} 

更新,響應由Yakk

發表評論我覺得Yakk的建議是有道理的。您可以刪除類CallbackRealCallback

using Callback = std::function<void()>; 

std::vector<Callback> callbackList; 

然後,add可以簡化爲:

template<class T> 
void add(T* object, void(T::*ptr)()) { 
    callbackList.push_back([object, ptr]{ (object->*ptr)();}); 
} 

這些變化,main需要稍微更新爲:

int main() { 
    //add to list for later use 
    add(&myobj, &A::myfunc); 
    add(&myobj2, &B::myfunc2); 

    std::cout << "myobj: " << &myobj << std::endl; 
    std::cout << "myobj2: " << &myobj2 << std::endl; 

    // Updated. Can't use callbackList[0]->call(); 

    //call them back 
    callbackList[0](); 
    callbackList[1](); 
} 
+0

謝謝你的快速和明智的答案,我很高興看到我的解決方案的改進版本。我正在考慮如何跳轉和存儲這些'類型變量'指針。我想過C++ 14提供的模板變量,但我真的不想使用C++ 14來實現可移植性。謝謝,'抽象基類'存儲方法真的很聰明。 –

+1

@ÖmerGöktaş,不客氣。請注意,我在基類中添加了一個'virtual'析構函數。 –

+1

我不明白爲什麼你要去實現'std :: function'的弱版本而不是......只是使用'std :: function'? – Yakk

4

Callback替換爲std::function<void()>

更換add

template<class T, class R, class U> 
void add(T* object, R(U::*ptr)()) { 
    Callback cb = [object, ptr]{ object->ptr(); }; 
    callbackList.push_back(cb); 
    // or just 
    // callbackList.push_back([object, ptr]{ object->ptr(); }); 
} 

注意,這支持傳遞指針到父成員函數,並且不返回void並丟棄結果的回調。

std::function存儲通用的「稍後調用」。您在模板簽名參數std::function<signature>中傳遞與返回值兼容的類型,並且與您希望稍後調用的參數兼容。在這種情況下,<void()>

+0

'callbackList.push_back(std :: bind(ptr,object));'? –

+0

@MooingDuck爲什麼要使用'std :: bind'?我的意思是,永遠? – Yakk

0

試着用std::functionstd::bind兩者需要保持參考實例:

#include <string> 
#include <iostream> 
#include <functional> 

using namespace std; 
class MyClass 
{ 
    int _value; 
public: 
    MyClass(int value) 
    { 
     _value = value; 
    } 

    void food() 
    { 
     cout << "Foo is doing something whit value: " << _value << endl; 
    } 
    void bar() 
    { 
     cout << "Bar is doing something whit value: " << _value << endl; 
    } 
}; 


int main() 
{ 

    MyClass* c1 = new MyClass(1); 
    MyClass* c2 = new MyClass(2); 

    cout << "Using 'std::function':" << endl; 

    std::function<void(MyClass&)> food = &MyClass::food; 
    std::function<void(MyClass&)> bar = &MyClass::bar; 

    food(*c1); 
    bar(*c1); 
    food(*c2); 
    bar(*c2); 


    cout << "Using 'std::bind':" << endl; 

    auto foodBind = std::bind(&MyClass::food, std::placeholders::_1); 
    auto barBind = std::bind(&MyClass::bar, std::placeholders::_1); 

    foodBind(*c1); 
    barBind(*c1); 
    foodBind(*c2); 
    barBind(*c2); 

    system("PAUSE"); 
}; 

輸出是:

enter image description here

相關問題