Указатели на функции-члены и наследование - PullRequest
9 голосов
/ 29 марта 2012

Мне нужно решить такую ​​проблему.Существует базовый класс и два унаследованных класса.Базовый класс содержит метод, который нуждается в указателе функции в качестве параметра.Но такие функции определены в унаследованных классах.

class CBase;

typedef bool (CBase::*FPredicate)();

class CBase
{
public:
    CBase() {}
    ~CBase() {}
protected:
    //this method waits until 'predicate' is true or until 'timeout' ms. passed
    //and returns true if 'predicate' is true eventually
    bool WaitEvent(FPredicate predicate, int timeout)
    {
        bool result = false;
        int time1 = GetTickCount();
        int time2;

        bool isEnd = false;
        while(!isEnd)
        {
            result = isEnd = (this->*predicate)();              

            time2 = GetTickCount();
            if(time2 - time1 > timeout && !isEnd)
                isEnd = true;
        }
        return result;
    }
};

class CChildA : public CBase
{
protected:
    bool a1() {/*some work*/}
    bool a2() {/*some work*/}
    void a_main()
    {
        ...
        WaitEvent(&CChildA::a1, 100);
        ...
        WaitEvent(&CChildA::a2, 100);
        ...
    }
};

class CChildB : public CBase
{
protected:
    bool b1() {/*some work*/}
    bool b2() {/*some work*/}
    void b_main()
    {
        ...
        WaitEvent(&CChildB::b1, 100);
        ...
        WaitEvent(&CChildB::b2, 100);
        ...
    }
};

Компилятор MSVC 2005 выдает ошибку при вызовах WaitEvent:

ошибка C2664: «CBase :: WaitEvent»: невозможно преобразовать параметр 1 из «bool» (__thiscall CChildA ::*) (void) 'to' FPredicate '

Вопрос: как мне изменить код, чтобы он работал?будет ли безопасно переписать вызов WaitEvent как WaitEvent((FPredicate)(&CChildA::a1), 100)?

В этом случае компилятор не сообщит об ошибке, но безопасно ли это?Или есть лучший способ решения проблемы?

Заранее спасибо.

Ответы [ 2 ]

3 голосов
/ 29 марта 2012

Вы можете сделать это, используя класс шаблона, чтобы сделать закрытие вашего дочернего объекта и его члена функции, сохранив его правильный тип.И затем использование виртуальных функций, чтобы базовый класс вызывал его через обычный полиморфизм.

Аналогичный механизм используется в shared_ptr для вызова деструкторов.Смотри: http://channel9.msdn.com/Shows/Going+Deep/C9-Lectures-Stephan-T-Lavavej-Advanced-STL-1-of-n

#include <iostream> 

struct CPredicateBase
{
        virtual ~CPredicateBase() {}
        virtual bool operator()() = 0;
};

template <class T>
struct CPredicate : public CPredicateBase
{
        bool (T::*func)();
        T* self;

        CPredicate(T* self_, bool (T::*func_)())
        : func(func_), self(self_) {}

        bool operator() () { return (self->*func)(); }
};

class CBase
{
public:

        bool WaitEvent(CPredicateBase& predicate, int imeout)
        {
                /// just to show the call
                bool b = predicate();
                std::cout << "WaitEvent called predicate() => " << b << std::endl;
                return b;
        }
};


class CChildA : public CBase
{
public:
        bool a1() { return false; }
        bool a2() { return true; }

        void a_main()
        {
                std::cout << "CChildA::a_main()" << std::endl;
                CPredicate<CChildA> caller1(this, &CChildA::a1);
                bool ra1 = WaitEvent(caller1, 100);
                CPredicate<CChildA> caller2(this, &CChildA::a2);
                bool ra2 = WaitEvent(caller2, 100);
        }
};

class CChildB : public CBase
{
public:
        bool b1() { return false; }
        bool b2() { return true; }

        void b_main()
        {
                std::cout << "CChildB::b_main()" << std::endl;
                CPredicate<CChildB> caller1(this, &CChildB::b1);
                bool rb1 = WaitEvent(caller1, 100);
                CPredicate<CChildB> caller2(this, &CChildB::b2);
                bool rb2 = WaitEvent(caller2, 100);
        }
};

int main(int argc, char const* argv[])
{
        CChildA cA;
        CChildB cB;

        cA.a_main();
        cB.b_main();

        return 0;
}
3 голосов
/ 29 марта 2012

Проблема в том, что неявно переданное это отличается по типу. Либо вы разыгрываете его, но это, вероятно, потерпит неудачу при наличии множественного наследования. Лучшим и более надежным решением было бы изменить подпись на:

template< typename T >
bool WaitEvent( bool ( T::*predicate )(), int timeout ) { ... }
...