【问题标题】:Member-function pointers and inheritance成员函数指针和继承
【发布时间】:2012-04-12 22:10:07
【问题描述】:

我需要解决这样的问题。 有一个基类和两个继承类。基类包含需要函数指针作为参数的方法。但是这样的函数是在继承的类中定义的。

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)' 转换为 'FPredicate'

一个问题是:我应该如何更改代码以使其工作?将 WaitEvent 调用重写为是否安全 WaitEvent((FPredicate)(&CChildA::a1), 100)?

在这种情况下,编译器不会报告任何错误,但它安全吗?或者有没有更好的解决问题的方法?

提前谢谢你。

【问题讨论】:

  • 有没有机会使用 boost 或 std::tr1?在这种情况下,您只需使用函数 并在派生类中使用带有成员函数的bind()
  • @stijn,记住,C++11 已经上线,bind() 应该已经在 std:: 中了。使用 -std=c++0x 通常是有益的,因为一些有助于日常编程的小功能已经可用。
  • 你能把派生类中的可调用函数从基类虚拟化吗?
  • 不幸的是,我从未使用过 boost,我不想使用整个大型新库来解决这个问题。
  • @Griwes 是的,但操作没有标记 C++11,我认为 SO 的一般规则是,如果你想要 C++11,你应该这样标记?

标签: c++ inheritance member-function-pointers


【解决方案1】:

问题在于隐式传递的 this 类型不同。要么你投它,但在存在多重继承的情况下这可能会失败。一个更好、更健壮的解决方案是将签名更改为:

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

【讨论】:

  • 谢谢,我想我会按照我的第一篇文章中描述的那样制作演员表。顺便说一句,我进行了一些测试,它们都很好。所以不会有多重继承,所以不会出现问题。
【解决方案2】:

您可以使用模板类来关闭您的子对象及其函数成员,从而保存它的正确类型。然后使用虚函数让基类通过通常的多态调用它。

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;
}

【讨论】:

    猜你喜欢
    • 2016-06-24
    • 2010-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-05
    • 1970-01-01
    相关资源
    最近更新 更多