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

使用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类中添加成员函数统一处理移动,避免外部直接操作内部控件:

  1. 在scene_button.h的public区域添加函数声明:
void MoveTo(int x, int y) {
    button->move(x, y);
    label->move(x, y);
    this->x = x;
    this->y = y;
}
  1. 修改创建对象时的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 07:01:06