调用QWidget::show()时Qt应用崩溃问题求助
本人是Qt新手,已知此类问题有相关报告但现有解答对我无用。尝试用CRTP实现图形界面模块,测试发现代码运行一切正常,直到调用Game::show()中的scene.window().view()->show()时应用崩溃,崩溃报告未提供有效排查线索。
代码情况
main.cpp
#include <QApplication> #include "game.h" int main(int argc, char* argv[]) { QApplication app(argc, argv); Game game; game.show(); return app.exec(); }
game.h
#ifndef GAME_H #define GAME_H #include "scene.h" class Game { public: Game(); void show(); private: GameScene scene; }; #endif // GAME_H
scene.h
#ifndef SCENE_H #define SCENE_H #include "GraphicsManager.h" struct GameScene { static GraphicsManager<MainWindow> window() { return win; } private: static GraphicsManager<MainWindow> win; }; #endif // SCENE_H
game.cpp
#include "game.h" Game::Game() { scene.window().render(); } void Game::show() { qDebug() << "crash test before show"; scene.window().view()->show(); // crashes here qDebug() << "crash test after show"; }
GraphicsManager.h
#ifndef GRAPHICSMANAGER_H #define GRAPHICSMANAGER_H #include <QGraphicsScene> #include <QGraphicsView> // inherits QWidget #include <QGraphicsPixmapItem> #include <QImage> #include <QBrush> template <typename T> struct GraphicsManager { void render() { static_cast<T*>(this)->render(); } QGraphicsScene* scene() { return static_cast<T*>(this)->scene(); } QGraphicsView* view() { return static_cast<T*>(this)->view(); } }; struct MainWindow : GraphicsManager<MainWindow> { void render() { m_scene = new QGraphicsScene(0, 0, 800, 800); m_view = new QGraphicsView(m_scene); m_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_view->setFixedSize(800, 800); } QGraphicsScene* scene() { return m_scene; } QGraphicsView* view() { return m_view; } private: QGraphicsScene* m_scene; // raw pointer is used here, since QGraphicsView* m_view; // they both inherit QObject, }; #endif // GRAPHICSMANAGER_H
编译日志
11:05:06: Running steps for project test3... 11:05:06: Starting: "C:\CMake\bin\cmake.exe" --build C:/a_Skola/Programmeringsmetodik/space_invaders_testsite/build-test3-Desktop_Qt_6_4_1_MinGW_64_bit-Debug --target all ninja: no work to do. 11:05:06: The process "C:\CMake\bin\cmake.exe" exited normally. 11:05:06: Elapsed time: 00:00.
问题分析与修复
崩溃的核心原因有两个:
对象切片与类型错误
GameScene中定义的静态成员win是GraphicsManager<MainWindow>类型,但MainWindow是继承自GraphicsManager<MainWindow>的派生类。当你通过window()返回基类对象时,会发生对象切片——派生类特有的成员(m_scene、m_view)会被丢弃。后续调用render()或view()时,static_cast<T*>(this)会把基类对象指针强制转换成派生类指针,这属于未定义行为,直接导致访问非法内存崩溃。返回对象副本而非引用
window()返回的是对象副本,而非引用。这意味着每次调用window()都会创建一个新的GraphicsManager<MainWindow>对象:Game::Game()中调用的render()是在一个临时基类对象上执行的,而Game::show()中调用的view()是在另一个全新的临时对象上执行的,两个对象毫无关联,自然无法获取已初始化的QGraphicsView指针。
修复步骤:
- 修改
scene.h,修正静态成员类型与返回值
把win的类型改为MainWindow,并让window()返回引用,避免对象切片和副本创建:
#ifndef SCENE_H #define SCENE_H #include "GraphicsManager.h" struct GameScene { static MainWindow& window() { return win; } // 返回引用 private: static MainWindow win; // 类型改为派生类MainWindow }; #endif // SCENE_H
- 添加静态成员定义
在项目中新建scene.cpp文件,定义静态成员win(C++中类静态成员必须在类外定义):
#include "scene.h" MainWindow GameScene::win;
- 可选:优化Qt对象内存管理
在MainWindow::render()中利用Qt父子对象机制自动管理内存,避免内存泄漏:
void render() { m_scene = new QGraphicsScene(0, 0, 800, 800); m_view = new QGraphicsView(m_scene); // QGraphicsScene会成为QGraphicsView的父对象 m_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_view->setFixedSize(800, 800); }
完成上述修改后,CRTP的静态转换会指向正确的MainWindow对象,render()初始化的m_scene和m_view也能被正确访问,调用view()->show()就不会崩溃了。
内容的提问来源于stack exchange,提问作者proxylite

