为了在 C++ 中热身,我正在 WinApi 上编写一个小型 OOP 插件,以便您可以在几行代码中创建窗口并执行必要的基本操作(例如更改文本、大小、在控件上设置事件、等等)。计划它的用途是这样的:
WqWindow::WqBegin();
WqWindow w;
WqButton b(&w);
WqTextBox tb(&w);
tb.SetPosition(WqPosition(50, 10))
->SetSize(WqSize(200, 25))
->SetText("");
b.SetText("Cool button")
->SetSize(WqSize(150, 25))
->SetPosition(WqPosition(50, 50))
->SetAcnhor(WqControlAnchor(false, false, true, false))
->SetOnClick([&b, &tb, &w]() { cout << "Clicked!" << endl; });
w.SetTitle("Cool window")
->SetSize(WqSize(400, 400))
->ClosesProgram(false)
->SetOnClose([]() { cout << "Closed!" << endl; return true; })
->Show();
WqWindow::WqEnd();
原理是这样的——创建控件时,将指向它的指针添加到窗口对象(WqWindow)的元素向量中,删除时,在析构函数中,从向量中删除该元素。大致是这样的
//Убираем из списка элементов управления окна
this->window_->controls_.erase(std::remove(this->window_->controls_.begin(), this->window_->controls_.end(), this), this->window_->controls_.end());
按钮和其他元素本身的事件在窗口过程中处理。原理大致如下:我们获取导致事件的窗口句柄(HWND),使用自定义指针(在创建 WqWindow 窗口时分配),我们获取关联的 WqWindow 对象并访问控制向量。我们循环遍历它们,并调用这些元素的 lambda 函数。它看起来像这样:
case WM_COMMAND:
if (wqWindow && !wqWindow->controls_.empty()) {
const std::vector<WqControl*> safeControlPointers(wqWindow->controls_);
for (WqControl * control : safeControlPointers)
{
if (control->initialized_)
{
if (HIWORD(wParam) == EN_CHANGE) {
if (control && control->ControlClassName() == "Edit" && control->GetHWND() == (HWND)(lParam)) {
WqTextBox * pTextBox = ((WqTextBox*)control);
if (pTextBox->onChanged_) {
pTextBox->onChanged_();
}
}
}
else {
if (control->ControlClassName() == "Button" && control->GetHWND() == (HWND)(lParam)) {
WqButton * pButton = ((WqButton*)control);
if (pButton->onClick_) {
pButton->onClick_();
}
}
}
}
}
}
break;
然后我想 - 如果这个库的用户想要删除(通过调用析构函数)lamb 表达式中的一些控件,例如像这样:
->SetOnClick([&b, &tb, &w]() { tb.~WqTextBox(); });
在这种情况下,我只是添加了 safeControlPointers 变量,这样当通过向量时,我们就像使用向量的副本一样工作,而不是使用大小会改变的向量。似乎一切都应该井井有条,但在执行过程中(按下按钮时)仍然发生错误。事实是,在向量的副本中有一个指向某个对象的指针,该对象以某种方式被删除了。我不知道怎么做,但以下内容帮助了我——我在 WqControl 中声明了 control->initialized_ 标志,它在构造函数中变为真,并在析构函数中设置为flase。在循环中,您可能注意到了检查
if (control->initialized_)
正是由于这一点,按下按钮时程序没有中断。我不完全明白为什么(毕竟,对象被破坏,从内存中删除,所以访问它的任何成员是不可能的)。如果你能解释这是怎么可能的,我会很高兴。好吧,我并没有就此止步,而是决定尝试创建一个 TextBox(我后来决定单击时删除),而不是在堆栈上,而是在堆上(即使用 new 运算符),然后调用 delete on点击。这就是一切最终崩溃的地方。并且没有检查帮助(指针空虚和其他)。
问题:如何实现类似的机制,以便在单击时可以删除其他元素?解决这个问题的正确方法是什么?提前致谢。
谢谢大家的建议,看来问题已经解决了。我刚刚摆脱了处理 WM_COMMAND 消息的循环。每个控件都有一个自定义对象指针(它包含所有必要的回调函数)。由于某种原因,我忘记了它。我只是获取控件的句柄,然后从中获取此指针,并调用必要的函数。现在看起来像这样:
结果,循环不会开始,从向量中删除时也不会发生错误。事实上,事实证明,没有必要存储指针列表。至少目前看来是这样。