从静态函数调用虚函数
Calling virtual function from static function
案例
我的基础class"Control"子classes WinAPI按钮控件:
hWndControl = CreateWindowEx
(
0
, L"BUTTON"
, L"Button"
, WS_VISIBLE | WS_CHILD | BS_OWNERDRAW | WS_EX_TRANSPARENT
, wndRc.left
, wndRc.top
, wndRc.right
, wndRc.bottom
, hWndParent
, 0
, hInstance
, 0
);
void* p_this{reinterpret_cast<void*>(this)}; // avoiding C-style cast
SetWindowSubclass
(
hWndControl
, Control::ControlProc
, 0
, reinterpret_cast<DWORD_PTR>(p_this)
)
据我所知,这要求我将回调定义为静态的(我这样做了)。
回调示例供参考:
LRESULT CALLBACK Control::ControlProc
(
HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
, UINT_PTR uIdSubclass
, DWORD_PTR dwRefData
)
{
// RETRIEVE POINTER TO THIS CLASS OBJECT
void* p_thisV{reinterpret_cast<void*>(dwRefData)}; // avoiding C-style cast
Control* const p_this{reinterpret_cast<Control*>(p_thisV)};
// PROCESS MESSAGES
switch (msg)
{
// DRAWING
case MY_DRAWITEM: // custom message forwarding WM_DRAWITEM from main window
{
p_this->DrawControl();
}
break;
...
}
return DefSubclassProc(hWnd, msg, wParam, lParam);
}
到目前为止,如果我在回调函数中或在回调中引用的基 class 中定义的成员函数中进行绘图,一切正常。
但我打算继承这个基础 class 用于具有不同外观的多个不同控件,同时使用相同的回调。所以我想我会创建在回调的特定点调用的虚函数,我可以在派生 class 中覆盖它,并为每个派生 class 使用自定义行为,如下所示:
// Base class header
class Control
{
...
protected:
virtual void DrawControl();
...
};
// Derived class header
class CalendarItem : public Control
{
...
protected:
void DrawControl();
...
};
// Derived class cpp
void CalendarItem::DrawControl()
{
std::unique_ptr<DrawBg> drawBg = std::unique_ptr<DrawBg>(new DrawBg(Control::hWndControl));
// this is the actual drawing mechanism, works, not relevant
}
问题
我在在线回调函数中遇到异常:p_this->DrawControl();
异常文本:p_this->**** 为 0x75004D。
你能告诉我如何修复这个解决方案吗,或者这样的事情是否可行?
ControlProc()
期望收到一个 Control*
指针,而不是 CalendarItem*
指针,或任何其他派生的 class 指针。因此,在转换 this
时,您需要先将其转换为有效的 Control*
指针 ,然后按原样将其转换为 DWORD_PTR
SetWindowSubclass()
,然后在 ControlProc()
中,您可以将 DWORD_PTR
直接转换回 Control*
。您根本不需要也不应该转换为中间值 void*
。
Control* p_this = this; // implicit conversion, no explicit cast needed
SetWindowSubclass
(
hWndControl,
Control::ControlProc,
0,
reinterpret_cast<DWORD_PTR>(p_this)
);
LRESULT CALLBACK Control::ControlProc
(
HWND hWnd,
UINT msg,
WPARAM wParam,
LPARAM lParam,
UINT_PTR uIdSubclass,
DWORD_PTR dwRefData
)
{
// RETRIEVE POINTER TO THIS CLASS OBJECT
Control* const p_this = reinterpret_cast<Control*>(dwRefData);
...
return DefSubclassProc(hWnd, msg, wParam, lParam);
}
RbMm 提示了正确的解决方案 - this
指针在堆栈上,我们需要堆指针以便它在回调函数运行时保留在内存中(因此当前 运行 函数已经完成)。
正确解法:
正在创建派生的 class 对象:
// provide base class pointer stored on heap
Derived* der = new Derived;
der->CreateInDerived(&(*der), ...); // "&(*der)" gets base class ptr from derived ptr
派生 class 函数:
void Derived::CreateInDerived(Base* ptr, ...)
{
ptr->CreateInBase(ptr, ...);
}
基础class函数:
void Base::CreateInBase(Base* ptr, ...)
{
...
SetWindowSubclass
(
hWndControl
, Control::ControlProc
, 0
, reinterpret_cast<DWORD_PTR>(ptr)
)
...
}
解释:
问题根本不在虚函数和静态函数的冲突上,而是在传递指针的生命周期上。指针地址在函数A中变成了一个数字,这样就可以通过DWORD参数给回调函数B了。
当函数 B 试图从数字中检索指针地址时,函数 A 中定义的指针已经超出范围(并且可能被覆盖,因为函数 A 完成并释放了内存)。
案例
我的基础class"Control"子classes WinAPI按钮控件:
hWndControl = CreateWindowEx
(
0
, L"BUTTON"
, L"Button"
, WS_VISIBLE | WS_CHILD | BS_OWNERDRAW | WS_EX_TRANSPARENT
, wndRc.left
, wndRc.top
, wndRc.right
, wndRc.bottom
, hWndParent
, 0
, hInstance
, 0
);
void* p_this{reinterpret_cast<void*>(this)}; // avoiding C-style cast
SetWindowSubclass
(
hWndControl
, Control::ControlProc
, 0
, reinterpret_cast<DWORD_PTR>(p_this)
)
据我所知,这要求我将回调定义为静态的(我这样做了)。 回调示例供参考:
LRESULT CALLBACK Control::ControlProc
(
HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
, UINT_PTR uIdSubclass
, DWORD_PTR dwRefData
)
{
// RETRIEVE POINTER TO THIS CLASS OBJECT
void* p_thisV{reinterpret_cast<void*>(dwRefData)}; // avoiding C-style cast
Control* const p_this{reinterpret_cast<Control*>(p_thisV)};
// PROCESS MESSAGES
switch (msg)
{
// DRAWING
case MY_DRAWITEM: // custom message forwarding WM_DRAWITEM from main window
{
p_this->DrawControl();
}
break;
...
}
return DefSubclassProc(hWnd, msg, wParam, lParam);
}
到目前为止,如果我在回调函数中或在回调中引用的基 class 中定义的成员函数中进行绘图,一切正常。
但我打算继承这个基础 class 用于具有不同外观的多个不同控件,同时使用相同的回调。所以我想我会创建在回调的特定点调用的虚函数,我可以在派生 class 中覆盖它,并为每个派生 class 使用自定义行为,如下所示:
// Base class header
class Control
{
...
protected:
virtual void DrawControl();
...
};
// Derived class header
class CalendarItem : public Control
{
...
protected:
void DrawControl();
...
};
// Derived class cpp
void CalendarItem::DrawControl()
{
std::unique_ptr<DrawBg> drawBg = std::unique_ptr<DrawBg>(new DrawBg(Control::hWndControl));
// this is the actual drawing mechanism, works, not relevant
}
问题
我在在线回调函数中遇到异常:p_this->DrawControl();
异常文本:p_this->**** 为 0x75004D。
你能告诉我如何修复这个解决方案吗,或者这样的事情是否可行?
ControlProc()
期望收到一个 Control*
指针,而不是 CalendarItem*
指针,或任何其他派生的 class 指针。因此,在转换 this
时,您需要先将其转换为有效的 Control*
指针 ,然后按原样将其转换为 DWORD_PTR
SetWindowSubclass()
,然后在 ControlProc()
中,您可以将 DWORD_PTR
直接转换回 Control*
。您根本不需要也不应该转换为中间值 void*
。
Control* p_this = this; // implicit conversion, no explicit cast needed
SetWindowSubclass
(
hWndControl,
Control::ControlProc,
0,
reinterpret_cast<DWORD_PTR>(p_this)
);
LRESULT CALLBACK Control::ControlProc
(
HWND hWnd,
UINT msg,
WPARAM wParam,
LPARAM lParam,
UINT_PTR uIdSubclass,
DWORD_PTR dwRefData
)
{
// RETRIEVE POINTER TO THIS CLASS OBJECT
Control* const p_this = reinterpret_cast<Control*>(dwRefData);
...
return DefSubclassProc(hWnd, msg, wParam, lParam);
}
RbMm 提示了正确的解决方案 - this
指针在堆栈上,我们需要堆指针以便它在回调函数运行时保留在内存中(因此当前 运行 函数已经完成)。
正确解法:
正在创建派生的 class 对象:
// provide base class pointer stored on heap
Derived* der = new Derived;
der->CreateInDerived(&(*der), ...); // "&(*der)" gets base class ptr from derived ptr
派生 class 函数:
void Derived::CreateInDerived(Base* ptr, ...)
{
ptr->CreateInBase(ptr, ...);
}
基础class函数:
void Base::CreateInBase(Base* ptr, ...)
{
...
SetWindowSubclass
(
hWndControl
, Control::ControlProc
, 0
, reinterpret_cast<DWORD_PTR>(ptr)
)
...
}
解释:
问题根本不在虚函数和静态函数的冲突上,而是在传递指针的生命周期上。指针地址在函数A中变成了一个数字,这样就可以通过DWORD参数给回调函数B了。
当函数 B 试图从数字中检索指针地址时,函数 A 中定义的指针已经超出范围(并且可能被覆盖,因为函数 A 完成并释放了内存)。