gpt4 book ai didi

c++ - 调用虚函数时崩溃

转载 作者:太空宇宙 更新时间:2023-11-04 14:58:23 25 4
gpt4 key购买 nike

好吧,这是一个非常奇怪的问题。首先,我想说我不是 C++ 的初学者,当然也不是高级的。我在中间的某个地方。我想要做的是制作 Win32 API 的 C++ OOP 包装器库 (dll)。这是我的图书馆的类(class)。我使用命令用 Mingw 编译了它:

g++ -shared -o bin\win32oop.dll src\Application.cpp src\Form\Form.cpp -Wall

源\应用程序.h:

#ifndef WOOP_APPLICATION_H_
#define WOOP_APPLICATION_H_

namespace Woop
{
class Application
{
public:
bool Init(void);
virtual bool OnInit(void);
};
}

#endif // WOOP_APPLICATION_H_

源\应用程序.cpp

#include <windows.h>
#include "Application.h"
#include "Form\Form.h"

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

namespace Woop
{
bool Application::Init(void)
{
WNDCLASSEX wc;

wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = GetModuleHandle(NULL);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = "woop";
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

if(RegisterClassEx(&wc) == 0)
{
MessageBox(NULL, "Window Registration Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
return false;
}

this->OnInit();

return true;
}

bool Application::OnInit(void)
{
return true;
}
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
Woop::Form *wnd = 0;

if (uMsg == WM_NCCREATE)
{
SetWindowLong (hwnd, GWL_USERDATA, long((LPCREATESTRUCT(lParam))->lpCreateParams));
}

wnd = (Woop::Form *)(GetWindowLong (hwnd, GWL_USERDATA));

if (wnd) return wnd->WndProc(hwnd, uMsg, wParam, lParam);

return ::DefWindowProc (hwnd, uMsg, wParam, lParam);
}

源\窗体\窗体.h

#ifndef WOOP_FORM_FORM_H_
#define WOOP_FORM_FORM_H_

namespace Woop
{
class Form
{
public:
bool Show(void);
virtual LRESULT WndProc(HWND, UINT, WPARAM, LPARAM);
protected:
HWND _handle;
};
}

#endif // WOOP_FORM_FORM_H_

源\窗体\窗体.cpp

#include <windows.h>
#include "Form.h"

namespace Woop
{
bool Form::Show(void)
{
_handle = CreateWindowEx(WS_EX_CLIENTEDGE, "woop", "", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, NULL, NULL, GetModuleHandle(NULL), this);

if(_handle == NULL)
{
MessageBox(NULL, "Window Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
return false;
}

ShowWindow(_handle, SW_SHOWNORMAL);

return true;
}

LRESULT Form::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}

这是我用来测试库的程序:

class SampleApp : public Woop::Application
{
bool OnInit(void)
{
Form form;
form.Show();

return true;
}
};

INT APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR, INT)
{
SampleApp application;
if(application.Init() == false) return 0;

MSG Msg;
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}

return 0;
}

好了,现在是问题。你在 Form 类中看到那个虚拟窗口过程了吗?如果我从声明中删除虚拟,程序编译并运行良好。但是当我把它加回去时,它崩溃了。出现臭名昭著的“不要发送”对话框。我不确定它什么时候崩溃,我会尝试使用 MessageBox() 来解决这个问题(哈哈,这是我没有学习如何使用 gdb 进行调试的结果)。我试图做到这一点,以便我可以创建一个类,例如 LoginForm 并从 Form 派生并覆盖 Window Procedure。我希望我足够好地解释了这个问题:D。这可能是编译器错误或我的愚蠢 :P。无论如何,提前致谢。

最佳答案

问题出在这里:

bool OnInit(void) 
{
Form form;
form.Show();

return true;
}

当该方法返回时,表单对象被销毁。
因此,您在调用 Show() 时存储的 this 指针不再有效。

  _handle = CreateWindowEx(WS_EX_CLIENTEDGE, "woop", "", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, NULL, NULL,
GetModuleHandle(NULL),
/* Here ----> */ this
);

当您尝试执行分派(dispatch)时,它真的搞砸了,因为它使用 this 指针来计算要调用的虚函数的地址。

它使用 virtual 而不是当你拿走 virtual 时崩溃的原因是虚拟方法地址是在运行时计算的,而普通方法地址是在编译时植入的。

当计算虚方法的地址时,this 指针以某种方式取消引用(在本例中导致 UB),但由于对象已被销毁,该地址处的数据可能已被删除重新使用,因此您获得的函数地址是一些随机垃圾,调用它永远不会好。

一个简单的解决方案是使表单成为应用程序对象的一部分。
因此它的生命周期与应用程序相同:

class SampleApp : public Woop::Application 
{
Form form;

bool OnInit(void)
{
form.Show();

return true;
}
};

关于c++ - 调用虚函数时崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3374277/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com