繁体   English   中英

是否可以在C ++中为顶级函数提供对对象成员的访问权限?

[英]Is it possible to give a top-level function access to an object's members in C++?

因此,我正在为Win32中的GUI编程编写一些包装器类。 我从Window类开始,到目前为止,它包含MainLoop方法,该方法基本上是标准Win32 WinMain函数的克隆。 这样,一个人可以做这样的事情:

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int nCmdShow) {
  Window *win = new Window();

  // Do all your widget creation and add it to the window object...

  return win->MainLoop(hInst, hPrev, szCmdLine, nCmdShow);
}

在窗口对象的MainLoop方法内部,它必须通过设置其lpfnWndProc成员来创建新的Win32窗口。 任何Win32程序员都知道,该成员是指向专门定义的WndProc函数的函数指针。 问题是,如果我要创建WndProc函数,则需要访问该窗口对象的成员(以便它知道在窗口上绘制什么内容,等等)。 这给了我两个选择(我知道):

  1. 我可以在顶层定义WndProc ,但这会切断对对象成员的访问。

  2. 我可以将其定义为类方法,但是它不是lpfnWndProc要求的确切函数类型,所以我无法设置它!

有人可以帮我解开这个catch-22吗?

您也可以使其成为静态成员函数。 :)
无论如何,一种解决方案取决于您只需要一个窗口还是需要多个窗口。 首先是单个窗口的解决方案:

// in .h
class Window{
public:
  static LRESULT WINAPI MessageProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
  LRESULT InternalMessageProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
  // ...
};

// in .cpp
#include "Window.h"

Window* global_window = 0;

Window::Window(/*...*/){
  if(!global_window)
    global_window = this;
  else
    // error or exception... or something else
}

LRESULT WINAPI Window::MessageProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){
  return global_window->InternalMessageProc(hWnd, msg, wParam, lParam);
}

现在,如果要允许多个窗口,请使用std::map (或如果编译器支持std::unordered_map )。
编辑 :此解决方案带有一些细微的问题。 正如@Ben Voigt在他的评论中指出的那样,由于MessageProcCreateWindow(Ex)内部被调用,您遇到了一个鸡与蛋的问题,但是只有在CreateWindow(Ex)调用之后,您才拥有窗口句柄。 这是基于Ben的下一条评论的解决方案(谢谢!):

// Window.h stays the same

// in .cpp
#include "Window.h"
#include <map>

std::map<HWND, Window*> window_map;
Window* currently_created_window = 0;

Window::Window(){
  currently_created_window = this;
  window_handle = CreateWindow(/*...*/);
}

LRESULT WINAPI Window::MessageProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){
  // if the key 'hWnd' doesn't exist yet in the map
  // a new key-value pair gets created and the value gets value-initialized
  // which, in case of a pointer, is 0
  if(window_map[hWnd] == 0){
    // window doesn't exist yet in the map, add it
    window_map[hWnd] = currently_created_window;
  }
  window_map[hWnd]->InternalMessageProc(hWnd, msg, wParam, lParam);
}

但是请谨慎,因为上面的示例不是线程安全的。 您需要互斥锁定窗口的创建:

Window::Window(/*...*/){
  Lock lock_it(your_mutex);
  currently_created_window = this;
  window_handle = CreateWindow(/*...*/);
  lock_it.release();
  // rest of the initialization
}

上面应该做的线程安全(我希望)。

您需要创建窗口地图,并在创建新窗口时将其添加到此全局地图中。 您可以使用简单的链接列表来代替课程。

map<HWND, Window *> wndmap;

LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wparam, LPARAM lparam)
{
    Window *pWnd = wndmap [hwnd];

    ....
}

WndProc不能是实例成员函数,因为Windows不会传递任何隐藏的this参数。 它可以是名称空间范围或静态成员。

一种简单的解决方案是使用map<HWND, Window*>查找对象,然后将参数转发给该对象上的方法。

请注意, WndProc可以维护地图本身,因为CreateWindow提供了一个不透明的用户参数,该参数显示在WM_CREATE ,这对于携带Window *很有用,然后您删除WM_DESTROY中的条目。

将WndProc定义为静态类成员-这样,它将与非成员函数指针(例如Win32编程中使用的那些)兼容(对于我知道的所有编译器)。

但是我不得不说这有点浪费时间-那里有无数的Windows类库,而且我认为世界上真的不需要另一个。

听起来您需要在不定义函数的情况下声明该函数。 那就是原型的目的。

class Object;

void f(Object* o);

class Object {
    public:
        ...
        void some_method() {
           ... &f ...
        }

        void another_method() {
           ...
        }

        ...
};

void f(Object* o) {
    ...
    o->another_method();
    ...
}

另一种可能的方式也是可能的。

class Object {
    public:
        ...
        void some_method();
        void another_method();
        ...
};

void f(Object* o) {
    ...
    o->another_method();
    ...
}

void Object::some_method() {
    ... &f ...
}

void Object::another_method() {
    ...
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM