注意:以下内容均基于个人理解,可能出现,误导,片面,错误等信息。请谨慎观看。
使用可变参数传入任意数量的成员函数指针
1.需求描述————————————————————————————————————————
最近在写一个C++控制台创建窗口项目,在封装类的时候想把 线程创建 和 创建窗口 以及 消息处理 封装在一个函数体内,这样在调用时候只需要一个方法就可以完成三件事。
void 想封装的创建窗口函数(){
thread 创建线程({
具体的创建窗口函数?();
消息处理要运行的函数?(); //消息处理必须和窗口创建在同一线程内
});
}
2.问题原因————————————————————————————————————————
但是我定义的 创建的窗口的方法 有很多,信息处理的方法也有很多种
并不知道在外部调用时会使用哪一个创建方法和处理方法
void 具体的创建窗口函数1()
void 具体的创建窗口函数2()
void 具体的创建窗口函数3()
.....
void 消息处理要运行的函数1()
void 消息处理要运行的函数2()
void 消息处理要运行的函数3()
.....
如果想用重载的方法来封装 会导致工作量太大 而且调用的时候很容易混淆
3.解决方案————————————————————————————————————————
于是打算想通过 函数指针 进行封装
即在外部调用时传入两个参数(函数指针),对应两个过程的函数,然后在创建线程中分别对这两个函数进行调用
这样就变成了
void 想封装的创建窗口函数 (void (*创建窗口函数指针)() , void (*消息处理函数指针)()){
thread 创建线程({
(*创建窗口函数指针)()
(*消息处理函数指针)()
})
}
//在外部调用时
类名 Classobject();
//创建一个窗口
Classobject.想封装的创建窗口函数(Classobject.具体的创建窗口函数1,Classobject.消息处理要运行的函数1);
//再创建一个不一样的窗口
Classobject.想封装的创建窗口函数(Classobject.具体的创建窗口函数3,Classobject.消息处理要运行的函数1);
这样写编译器是一定会报错的 因为
我要调用的函数为 成员函数 所以在使用函数指针时需要 给函数指针加上命名空间
注意: 在外部调用时候,取成员函数地址时要在命名空间 前 加上 取址符 &
&类名::*创建窗口函数指针
在函数体内部调用时候需要通过使用 this 指针来进行调用
(this->*创建窗口函数指针)()
于是现在函数变成了这样
void 想封装的创建窗口函数 (void (类名::*创建窗口函数指针)() , void (类名::*消息处理函数指针)()){
thread 创建线程({
(this->*创建窗口函数指针)()
(this->*消息处理函数指针)()
})
}
//在外部调用时
类名 Classobject();
Classobject.想封装的创建窗口函数( &类名::具体的创建窗口函数1 , &类名::消息处理要运行的函数1 );
——这样就完成了这个函数的封装
4.新的问题————————————————————————————————————————
但是在调用时候我很快发现了问题 就是这个方法中 消息处理函数有可能不只执行一种
就是:
void 想封装的创建窗口函数(???){
thread 创建线程({
具体的创建窗口函数?();
消息处理要运行的函数1(); //消息处理可能调用多个
消息处理要运行的函数3();
消息处理要运行的函数4();
消息处理要运行的函数8();
});
}
如果按照之前的函数指针写 就只能调用一个消息处理函数。
所以还要进行进一步调整
最后选择使用 可变参数 来传入不定数量的函数指针 这样就可以在函数体内部调用任意数量的处理函数
5.最终方案————————————————————————————————————————
使用 initializer_list <type> 来实现 可变参数 (具体参考-> 微软文档 WIndowsLearn :省略号和可变参数模板 )
传入的参数可以使用 迭代器进行遍历 ,然后直接调用就可以了 (文中直接进行了遍历)
void 想封装的创建窗口函数(void (类名::*创建窗口函数指针)() , initializer_list<void (类名::*)()> 消息处理函数列表){
thread 创建线程({
具体的创建窗口函数?();
for(auto 消息处理函数 : 消息处理函数列表 ){
(this->*消息处理函数)();
}
});
}
//在外部调用时
类名 Classobject();
Classobject.想封装的创建窗口函数( &类名::具体的创建窗口函数1 ,
{ &类名::消息处理要运行的函数1 , &类名::消息处理要运行的函数3 , &类名::消息处理要运行的函数4, &类名::消息处理要运行的函数8 } );
最后贴上我原本的代码
void CreatWnd(HWND(WindowsControl::* CreatWndFunction)(const WindowStyle&), const WindowStyle& Style, initializer_list<void (WindowsControl::*)()> MessageCallBacklist) {
thread th([this, CreatWndFunction, MessageCallBacklist, Style] {
(this->*CreatWndFunction)(Style);
MessageManager(MessageCallBacklist);
});
th.detach();
}
void MessageManager(initializer_list<void (WindowsControl::*)()> MessageCallBacklist) {
MSG msg = { 0 };
while (msg.message != WM_CLOSE) {
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
DispatchMessage(&msg);
}
for (auto i : MessageCallBacklist) {
(this->*i)();
}
}
}
//外部调用
WindowsControl wc("newclasses");
wc.CreatWnd(&WindowsControl::CreatWndOverLay, WindowStyle("ShadowStars",parentwnd), {&WindowsControl::FollowWindows});
6.总结————————————————————————————————————————
- 想通过函数指针进行封装
- 外部传入成员函数指针时 需要使用 &类名::成员函数名
- 内部调用成员函数指针时 需要使用 this 指针调用
- 通过 initializer_list <> 来传递可变参数
希望能帮到各位 辛苦点个赞叭 😃