使用Lambda作为SceneButton构造参数时程序崩溃问题排查
问题描述
我想实现点击SceneButton时将其移动到指定位置,编写了如下代码:
SceneButton *scene1_nightstand_pillow = new SceneButton( 6, 336, "nightstand_pillow", "./res/scene2_nightstand_pillow.png", [&scene1_nightstand_pillow, this]() { QMessageBox::information(this, "scene2", "pillow"); //scene1_nightstand_pillow->GetButton()->move(0, 0); }, this); scene1_nightstand->AddSceneButton(scene1_nightstand_pillow);
取消注释移动按钮的语句后,程序会崩溃(但消息框能正常显示)。我原本以为是构造函数执行时scene1_nightstand_pillow对象尚未完成初始化导致的,于是尝试编写SetOnClickFunction方法并在构造后调用,但问题依然存在。
以下是SceneButton类的头文件和实现文件:
scene_button.h
#ifndef SCENE_BUTTON_H #define SCENE_BUTTON_H #include <iostream> #include <functional> #include <QWidget> #include <QApplication> #include <QPushButton> #include <QLabel> #include <QMovie> #include <QMessageBox> class SceneButton : public QWidget { Q_OBJECT private: double x, y; double size_x, size_y; std::string path = ""; std::string name = ""; std::function<void()> onClick; QPushButton *button; QLabel *label; QMovie *movie; public: explicit SceneButton( double _x, double _y, double _size_x, double _size_y, std::string _name, std::string _path, std::function<void()> _onClick, QWidget *parent = nullptr, std::function<void()> _ondefine = []() {}); explicit SceneButton( double _x, double _y, std::string _name, std::string _path, std::function<void()> _onClick, QWidget *parent = nullptr, std::function<void()> _ondefine = []() {}); ~SceneButton(); std::string GetName() { return this->name; } std::string GetPath() { return this->path; } float GetX(){return x;} float GetY(){return y;} std::function<void()> GetOnClickFunction() const { return onClick; } QPushButton *GetButton() { return button; } QLabel *GetLabel() { return label; } }; #endif
scene_button.cc
#include "scene_button.h" SceneButton::SceneButton(double _x, double _y, double _size_x, double _size_y, std::string _name, std::string _path, std::function<void()> _onClick, QWidget *parent, std::function<void()> _ondefine) : QWidget(parent) { this->x = _x; this->y = _y; this->size_x = _size_x; this->size_y = _size_y; this->name = _name; this->path = _path; this->onClick = _onClick; QPixmap p(QString::fromStdString(path)); label = new QLabel(parent); button = new QPushButton(parent); movie = new QMovie(QString::fromStdString(path)); label->setMovie(movie); QString background_image = "image: url(" + QString::fromStdString(path) + ");"; QString style_sheet = "background-color: rgba(255, 255, 255, 0);"; label->setAttribute(Qt::WA_TranslucentBackground); label->setFixedSize(size_x, size_y); label->move(x, y); button->setStyleSheet(style_sheet); button->setFixedSize(size_x, size_y); button->move(x, y); button->setCursor(Qt::PointingHandCursor); QObject::connect(movie, &QMovie::frameChanged, [this](int frameNumber) { if (frameNumber == movie->frameCount()-1) { } }); QObject::connect(button, &QPushButton::clicked, onClick); movie->start(); _ondefine(); } SceneButton::SceneButton(double _x, double _y, std::string _name, std::string _path, std::function<void()> _onClick, QWidget *parent, std::function<void()> _ondefine) : QWidget(parent) { this->x = _x; this->y = _y; this->path = _path; this->onClick = _onClick; this->name = _name; QPixmap p(QString::fromStdString(path)); this->size_x = p.width(); this->size_y = p.height(); label = new QLabel(parent); button = new QPushButton(parent); movie = new QMovie(QString::fromStdString(path)); label->setMovie(movie); QString background_image = "image: url(" + QString::fromStdString(path) + ");"; QString style_sheet = "background-color: rgba(255, 255, 255, 0);"; label->setAttribute(Qt::WA_TranslucentBackground); label->setFixedSize(p.width(), p.height()); label->move(x, y); button->setStyleSheet(style_sheet); button->setFixedSize(p.width(), p.height()); button->move(x, y); button->setCursor(Qt::PointingHandCursor); QObject::connect(movie, &QMovie::frameChanged, [this](int frameNumber) { if (frameNumber == movie->frameCount()-1) { } }); QObject::connect(button, &QPushButton::clicked, onClick); movie->start(); _ondefine(); } SceneButton::~SceneButton() { delete button; delete label; delete movie; }
解决方案
崩溃的核心原因是你在lambda中捕获了scene1_nightstand_pillow的引用,而这个指针在构造时还未完成赋值,后续即使构造完成,引用也指向了错误的内存地址。
修复方案一:捕获指针值而非引用
修改lambda的捕获列表,把&scene1_nightstand_pillow改成scene1_nightstand_pillow,让lambda复制指针的正确值:
SceneButton *scene1_nightstand_pillow = new SceneButton( 6, 336, "nightstand_pillow", "./res/scene2_nightstand_pillow.png", [scene1_nightstand_pillow, this]() { QMessageBox::information(this, "scene2", "pillow"); scene1_nightstand_pillow->GetButton()->move(0, 0); }, this); scene1_nightstand->AddSceneButton(scene1_nightstand_pillow);
修复方案二:类内封装移动逻辑(更优雅)
在SceneButton类中添加成员函数统一处理移动,避免外部直接操作内部控件:
- 在
scene_button.h的public区域添加函数声明:
void MoveTo(int x, int y) { button->move(x, y); label->move(x, y); this->x = x; this->y = y; }
- 修改创建对象时的lambda,直接调用封装好的方法:
SceneButton *scene1_nightstand_pillow = new SceneButton( 6, 336, "nightstand_pillow", "./res/scene2_nightstand_pillow.png", [scene1_nightstand_pillow, this]() { QMessageBox::information(this, "scene2", "pillow"); scene1_nightstand_pillow->MoveTo(0, 0); }, this);
额外优化建议
你当前的button和label是直接创建在parent下的,而非SceneButton的子控件,这会导致布局管理混乱。建议修改构造函数中的创建逻辑,将父对象改为this:
// 在构造函数中替换原创建代码 label = new QLabel(this); button = new QPushButton(this);
这样移动SceneButton本身时,子控件会自动跟随移动,更符合Qt的控件层级设计。
内容的提问来源于stack exchange,提问作者EdmondAdams
相关产品推荐
相关产品推荐

