【问题标题】:how to clear memory from deleted object in c++?如何从 C++ 中的已删除对象中清除内存?
【发布时间】:2026-01-08 01:50:01
【问题描述】:

我想用 C++ 开发自己的信号范围。所以我使用 qt 来达到这个目的。 我在 ui 中添加了一个 graphicsView 和一个按钮。在按钮的连接方法中,我更新了 graphicsView(最后我将把这个方法传递给一个线程)。 每次我按下按钮时,尽管删除指针,内存的使用量都会增加。我应该如何控制?

我在 vs15 诊断工具中检查内存使用情况。

c++头文件:

#pragma once

#include <QtWidgets/QMainWindow>
#include "ui_QtGuiApplication.h"
#include <QGraphicsScene>
#include <QGraphicsPathItem>
class QtGuiApplication : public QMainWindow
{
    Q_OBJECT

public:
    QtGuiApplication(QWidget *parent = Q_NULLPTR);

private:
    Ui::QtGuiApplicationClass ui;

    QGraphicsScene* scene = new QGraphicsScene();
    QPolygon pol;
    QGraphicsPathItem* pathItem = new QGraphicsPathItem();
    int index_ = 0;            // graph offset
    QPen* myPen = new QPen(Qt::red, 2);

    private slots:  
    void btn_Display_clicked();
};

c++源文件:

    #include "QtGuiApplication.h"
#include <math.h>       /* sin */

#define pi 3.14159265

QtGuiApplication::QtGuiApplication(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);
    ui.graphicsView->setScene(scene);
    ui.graphicsView->show();
    connect(ui.btn_Display, SIGNAL(clicked()), this, SLOT(btn_Display_clicked()));
}

void QtGuiApplication::btn_Display_clicked()
{
    scene->removeItem(pathItem);
    QPoint pos;

    double x_amp_, y_amp_;
    int x_pix_, y_pix_;
    double phi_ = 0;
    double freq_ = 5;
    for (int i = 0; i<800; i++)
    {
        y_amp_ = 100 * sin(2 * pi*freq_*i / 811 + phi_) + 20 * index_;
        x_amp_ = i;
        x_pix_ = x_amp_;
        y_pix_ = y_amp_;
        pos.setX(x_pix_);
        pos.setY(y_pix_);
        pol.append(pos);
    }
    QPainterPath* myPath = new QPainterPath();
    (*myPath).addPolygon(pol);
    pathItem = scene->addPath(*myPath, *myPen);
    delete myPath;
    pol.clear();
    index_ = (index_ + 1) % 20; // just for sense of change in graph 
}

【问题讨论】:

  • 我不明白为什么您在将 myPath 添加 按值scene 时需要指针?
  • 代码调用scene-&gt;addPath。这可能会增加scene 的大小。
  • 每次我想删除以前的路径并绘制新路径。所以我想我必须按价值添加它。还有其他方法吗?
  • 在方法的开头使用行代码scene-&gt;removeItem(pathItem) 我认为添加的项目已被删除。
  • 您的代码无法编译 - 您似乎缺少 "ui_QtGuiApplication.h"。最好提供单个文件minimal reproducible example

标签: c++ multithreading qt memory qpainterpath


【解决方案1】:

pathItem = scene->addPath(*myPath, *myPen);

创建了一个新的QGraphicsPathItem,并返回了一个指向pathItem 的指针。迟早会再次单击该按钮并

scene->removeItem(pathItem);

从场景中移除QGraphicsPathItem。不幸的是according to the documentation

项目的所有权被传递给调用者(即,QGraphicsScene 在销毁时将不再删除项目)。

pathItem 的删除取决于程序员。 pathItem 未被删除并在随后调用时泄露

pathItem = scene->addPath(*myPath, *myPen);

解决方法:在pathItem被泄露之前将其删除。

scene->removeItem(pathItem);
delete pathItem; 

应该做的工作。

【讨论】:

    【解决方案2】:

    我有几个 cmets。

    1. 您正在做很多完全不必要的手动内存管理。为什么不好?因为如果你不是,你的错误将不可能通过构造。您对手动内存分配(显式new)的所有使用都是不必要的。按值保存对象:它们被设计为以这种方式工作,而让编译器担心其余的事情。

    2. 您在太大的范围内声明对象。您在类中声明多边形和笔,即使它们属于btn_Display_clicked 的范围;您在循环之外声明循环变量(x_amp_y_amp_)。

    3. connect 语句是多余的:setupUi 为您完成连接。

    4. 您没有充分利用 C++11。

    5. 包含&lt;QtModule/QClass&gt; 形式的内容将项目错误配置的检测推迟到链接时间,并且是不必要的。您应该直接包含&lt;QClass&gt;。如果你得到一个编译错误,那么你的项目配置错误——也许你需要重新运行 qmake(典型的解决方案)。

    6. 在 C++ 中,&lt;cmath&gt; 是惯用的,而不是 &lt;math.h&gt;。保留后者是为了与 C 兼容。

    7. 也许您可能希望使用M_PI。它是 C++ 中 pi 的一个广泛使用的名称。

    8. 预分配多边形可避免在其增长时重新分配点存储的过早悲观。

    9. 使用基于整数的多边形会降低精度,除非这是出于特定原因做出的选择。否则,您应该使用QPolygonF

    这样,界面就变成了:

    #pragma once
    
    #include <QMainWindow>
    #include <QGraphicsScene>
    #include <QGraphicsPathItem>
    #include "ui_QtGuiApplication.h"
    
    class GuiApplication : public QMainWindow
    {
       Q_OBJECT
    
    public:
       GuiApplication(QWidget *parent = {});
    
    private:
       Ui::GuiApplicationClass ui;
    
       QGraphicsScene scene;
       QGraphicsPathItem pathItem; // the order matters: must be declared after the scene
       int index_ = 0;
    
       Q_SLOT void btn_Display_clicked();
    };
    

    以及实现:

    #include "GuiApplication.h"
    #define _USE_MATH_DEFINES
    #include <cmath>
    
    #if !defined(M_PI)
    #define M_PI (3.14159265358979323846)
    #endif
    
    GuiApplication::GuiApplication(QWidget *parent) : QMainWindow(parent)
    {
       ui.setupUi(this);
       ui.graphicsView->setScene(&scene);
       ui.graphicsView->show();
       pathItem.setPen({Qt::red, 2});
       scene.addItem(&pathItem);
    }
    
    void GuiApplication::btn_Display_clicked()
    {
       const double phi_ = 0;
       const double freq_ = 5;
       const int N = 800;
       QPolygonF pol{N};
       for (int i = 0; i < pol.size(); ++i) {
          qreal x = i;
          qreal y = 100 * sin(2 * M_PI * freq_ * i / 811 + phi_) + 20 * index_;
          pol[i] = {x, y};
       }
       QPainterPath path;
       path.addPolygon(pol);
       pathItem.setPath(path);
       index_ = (index_ + 1) % 20; // just for sense of change in graph
    }
    

    在 C++14 中,如果您希望在其他地方重用生成器,您可以轻松地将其分解出来;那么循环将被替换为:

    auto generator = [=,i=-1]() mutable {
       ++i;
       return QPointF{qreal(i), 100 * sin(2 * M_PI * freq_ * i / 811 + phi_) + 20 * index_};
    };
    std::generate(pol.begin(), pol.end(), generator);
    

    唉,就目前而言,btn_Display_clicked 实现丢弃了分配用于存储多边形的堆,并且它不必要地将数据从多边形复制到路径内的内部元素存储。我们可以通过直接修改painter item的路径来避免这两种情况:

    void GuiApplication::btn_Display_clicked()
    {
       const double phi_ = 0;
       const double freq_ = 5;
       const int N = 800;
       if (pathItem.path().isEmpty()) { // preallocate the path
          QPainterPath path;
          path.addPolygon(QPolygon{N});
          pathItem.setPath(path);
       }
       auto path = pathItem.path();
       pathItem.setPath({}); // we own the path now - item's path is detached
       Q_ASSERT(path.elementCount() == N);
       for (int i = 0; i < path.elementCount(); ++i) {
          qreal x = i;
          qreal y = 100 * sin(2 * M_PI * freq_ * i / 811 + phi_) + 20 * index_;
          path.setElementPositionAt(i, x, y);
       }
       pathItem.setPath(path);
       index_ = (index_ + 1) % 20; // just for sense of change in graph
    }
    

    因为QPainterPath是一个隐式共享值类,所以path = item.path(); item.setPath({});序列相当于将路径移出路径项。随后的setPath(path) 将其移回,因为我们不会对本地副本进行进一步更改。

    【讨论】:

    • 感谢您的好建议。将来,我将使用 UDP 协议从 LAN 获取数据。所以这段代码的数学部分只是为了测试。实际工作中的像素变量必须用幅度进行缩放。所以我必须把它们放在循环中。我是电信工程师和 C++ 和 Qt 编程的初学者。我会考虑你的建议。谢谢。