成员函数指针和继承

时间:2012-04-05 00:16:42

标签: c++ winapi visual-studio-2010

所以我为了自己的方便而正在使用一个简单的win32包装器,而且我遇到了一个稍微复杂的问题。

这有很多其他成员,但我省略了一点,只留下了违规的成员。

class Windows::AbstractWindow
{
public:
     void InstallHandler(UINT msgName, void (Windows::AbstractWindow::*)(HWND, UINT, WPARAM, LPARAM));

private:
     std::map<UINT, void (Windows::AbstractWindow::*)(HWND, UINT, WPARAM, LPARAM)> HandlerIndex;

};

(对于记录,Windows在这种情况下是我所做的各种类和对象的命名空间)

只是有点讨厌,但是lemme解释了我的过程和推理。我有一个名为AbstractWindow的类,它以面向对象的方式包含窗口的大部分功能。

我现在正在开发一种获取私有成员函数的方法,并通过指向它们的指针将它们存储在映射中,这些指针由它们应该处理的Windows消息标识。这样,当windows过程收到消息时,它会遍历此映射以查看您是否为其安装了处理程序。如果它有它调用该功能并退出。它没有它调用DefWindowProc并退出。很容易。

但是,这个对象永远不应该被实例化,而应该继承和扩展。问题是,map的函数指针声明是AbstractWindow类型,它不允许我存储从AbstractWindow继承的类型的成员函数指针。例如,

class BasicWindow : public Windows::AbstractWindow
{
public:
    BasicWindow() 
    {
         InstallHandler(WM_CREATE, &create);
    }

private:
    void Create(HWND, UINT, WPARAM, LPARAM) {}
}

...产生错误:

error C2664: 'Windows::AbstractWindow::InstallHandler' : cannot convert parameter 2 from 'void (__thiscall BasicWindow::* )(HWND,MSG,WPARAM,LPARAM)' to 'void (__thiscall Windows::AbstractWindow::* )(HWND,UINT,WPARAM,LPARAM)'

因为指针类型不一样,尽管从基类继承。那么有人希望在保持这种方法的同时提出解决方案吗?如果没有,我也愿意接受你认为会使消息处理比这种方式更方便的建议。

3 个答案:

答案 0 :(得分:3)

您遇到的问题是您正试图以相反的方式进行转换。函数指针在它们的this参数中是逆变的(即,指向基类的函数指针&#39;函数将用于指向派生类&#39;方法的函数指针,反之亦然)。你可以:

  • 刚演员(使用static_cast,因为它与隐式转换相反)。只要你能确保你永远不会在不适当的类上调用该方法(例如NotABasicWindow().*method()),它就能正常工作。
  • 摆脱该方案,并注册一般函数(即任何可以调用而不是成员函数指针的函数)。你会用eg。 std::function<void(HWND, UINT, WPARAM, LPARAM)>作为您的处理程序类型并注册将知道其窗口的处理程序(例如,lambda函数)。

    • lambda functions are a feature of C++11。它们大致对应于带有粘合剂的函数对象;它们创建了一个匿名函数,可以引用它们所在范围内的变量。一个例子是

      [=](HWND wnd, UINT i, WPARAM wp, LPARAM lp) { this->printHandler(wnd, i, wp, lp); }
      

      会记住this,因此在调用时会调用printHandler来获取当前对象(创建lambda的代码的当前代码,而不是调用代码)。当然,应该调用该方法的对象可能只是另一个参数。

      Lambdas以及其他函数对象(即定义了operator()的对象)可以转换为std::function个对象并存储。

答案 1 :(得分:3)

您应该阅读奇怪的重复模板模式(http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)。

基本上,您将基类转换为带有指定子类的模板参数的模板。

如果你看到它可能会更容易理解:

namespace Windows
{
     template <typename T>
     class AbstractWindow
     {
     public:
          void InstallHandler(UINT msgName, void (T::*)(HWND, UINT, WPARAM, LPARAM));

     private:
          std::map<UINT, void (T::*)(HWND, UINT, WPARAM, LPARAM)> HandlerIndex;

     };
}

class BasicWindow : public Windows::AbstractWindow< BasicWindow >
{
public:
    BasicWindow() 
    {
         InstallHandler(WM_CREATE, &BasicWindow::Create);
    }

private:
    void Create(HWND, UINT, WPARAM, LPARAM) {}
};

答案 2 :(得分:0)

这种性质的东西能起作用吗? PS。我使用了typedef作为函数签名。

BasicWindow() 
    {
         InstallHandler(WM_CREATE, reinterpret_cast<void (__thiscall Windows::AbstractWindow::* )(HWND,UINT,WPARAM,LPARAM)>(&create));
    }
相关问题