You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用QWidget::show()时Qt应用崩溃问题求助

Qt CRTP实现GUI模块崩溃问题排查

本人是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.

问题分析与修复

崩溃的核心原因有两个:

  1. 对象切片与类型错误
    GameScene中定义的静态成员win是GraphicsManager<MainWindow>类型,但MainWindow是继承自GraphicsManager<MainWindow>的派生类。当你通过window()返回基类对象时,会发生对象切片——派生类特有的成员(m_scene、m_view)会被丢弃。后续调用render()或view()时,static_cast<T*>(this)会把基类对象指针强制转换成派生类指针,这属于未定义行为,直接导致访问非法内存崩溃。

  2. 返回对象副本而非引用
    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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 02:45:33